commit 18/09/2025
This commit is contained in:
302
html/webpage/assets/js/broadcastzones.js
Normal file
302
html/webpage/assets/js/broadcastzones.js
Normal file
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* @typedef {Object} BroadcastZone
|
||||
* @property {number} index
|
||||
* @property {string} description
|
||||
* @property {String} SoundChannel
|
||||
* @property {String} Box
|
||||
* @property {String} Relay
|
||||
*/
|
||||
|
||||
/**
|
||||
* List of broadcast zones available
|
||||
* @type {BroadcastZone[]}
|
||||
*/
|
||||
let BroadcastZoneList = [];
|
||||
|
||||
/**
|
||||
* Currently selected broadcast zone row in the table
|
||||
* @type {JQuery<HTMLElement>|null}
|
||||
*/
|
||||
let selectedBroadcastZoneRow = null;
|
||||
|
||||
/**
|
||||
* Fill broadcast zone table body with values
|
||||
* @param {BroadcastZone[]} vv values to fill
|
||||
*/
|
||||
function fill_broadcastzonetablebody(vv) {
|
||||
$('#broadcastzonetablebody').empty();
|
||||
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||
vv.forEach(item => {
|
||||
const row = `<tr>
|
||||
<td>${item.index}</td>
|
||||
<td>${item.description}</td>
|
||||
<td>${item.soundChannel}</td>
|
||||
<td>${item.box}</td>
|
||||
<td>${item.relay}</td>
|
||||
</tr>`;
|
||||
$('#broadcastzonetablebody').append(row);
|
||||
let $addedrow = $('#broadcastzonetablebody tr:last');
|
||||
$addedrow.click(function () {
|
||||
if (selectedBroadcastZoneRow) {
|
||||
selectedBroadcastZoneRow.find('td').css('background-color', '');
|
||||
if (selectedBroadcastZoneRow.is($(this))) {
|
||||
selectedBroadcastZoneRow = null;
|
||||
$('#btnRemove').prop('disabled', true);
|
||||
$('#btnEdit').prop('disabled', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$(this).find('td').css('background-color', '#ffeeba');
|
||||
selectedBroadcastZoneRow = $(this);
|
||||
$('#btnRemove').prop('disabled', false);
|
||||
$('#btnEdit').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
$('#tablesize').text("Table Size: " + vv.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload broadcast zones from server
|
||||
* @param {String} APIURL API URL endpoint (default "BroadcastZones/")
|
||||
*/
|
||||
function reloadBroadcastZones(APIURL = "BroadcastZones/") {
|
||||
BroadcastZoneList = [];
|
||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||
if (Array.isArray(okdata)) {
|
||||
//console.log("reloadBroadcastZones : ", okdata)
|
||||
BroadcastZoneList = okdata;
|
||||
fill_broadcastzonetablebody(BroadcastZoneList);
|
||||
} else console.log("reloadBroadcastZones: okdata is not array");
|
||||
}, (errdata) => {
|
||||
alert("Error loading broadcast zones : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log("broadcastzones.js loaded successfully");
|
||||
selectedBroadcastZoneRow = 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_BroadcastZone = "BroadcastZones/";
|
||||
|
||||
|
||||
let $broadcastzonemodal = $('#broadcastzonemodal');
|
||||
let $broadcastzoneindex = $broadcastzonemodal.find('#broadcastzoneindex');
|
||||
let $broadcastzonedescription = $broadcastzonemodal.find('#broadcastzonedescription');
|
||||
let $broadcastzonesoundchannel = $broadcastzonemodal.find('#broadcastzonesoundchannel');
|
||||
let $broadcastzonebox = $broadcastzonemodal.find('#broadcastzonebox');
|
||||
|
||||
|
||||
|
||||
let $findzone = $('#findzone');
|
||||
$findzone.on('input', function () {
|
||||
let searchTerm = $findzone.val().trim().toLowerCase();
|
||||
if (searchTerm.length > 0) {
|
||||
selectedBroadcastZoneRow = null;
|
||||
let filtered = broadcastzonedata.filter(item => item.description.toLowerCase().includes(searchTerm) || item.box.toLowerCase().includes(searchTerm) || item.soundChannel.toLowerCase().includes(searchTerm) || item.relay.toLowerCase().includes(searchTerm));
|
||||
fill_broadcastzonetablebody(filtered);
|
||||
} else {
|
||||
selectedBroadcastZoneRow = null;
|
||||
fill_broadcastzonetablebody(broadcastzonedata);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Find Checkbox for relays 1 to 32
|
||||
* Checkbox id is 01 to 32 with leading zero for 1 to 9
|
||||
* @param {number} id 1 - 32
|
||||
* @returns JQuery<HTMLElement>
|
||||
*/
|
||||
function cbRelay(id) {
|
||||
return $broadcastzonemodal.find('#R' + (id < 10 ? '0' : '') + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear broadcast zone modal to default state
|
||||
*/
|
||||
function clearBroadcastZoneModal() {
|
||||
$broadcastzoneindex.prop('disabled', true).val('');
|
||||
$broadcastzonedescription.val('');
|
||||
$broadcastzonesoundchannel.val('');
|
||||
$broadcastzonebox.val('');
|
||||
for (let i = 1; i <= 32; i++) {
|
||||
cbRelay(i).prop('checked', false);
|
||||
}
|
||||
}
|
||||
|
||||
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||
|
||||
$btnClear.click(() => {
|
||||
DoClear(APIURL_BroadcastZone, "BroadcastZones", (okdata) => {
|
||||
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||
alert("Success clear broadcast zones: " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error clear broadcast zones: " + errdata.message);
|
||||
});
|
||||
});
|
||||
|
||||
$btnAdd.click(() => {
|
||||
$broadcastzonemodal.modal('show');
|
||||
clearBroadcastZoneModal();
|
||||
|
||||
$broadcastzonemodal.off('click.broadcastzonesave').on('click.broadcastzonesave', '#broadcastzonesave', function () {
|
||||
|
||||
let description = $broadcastzonedescription.val().trim();
|
||||
let soundChannel = $broadcastzonesoundchannel.val().trim();
|
||||
let box = $broadcastzonebox.val().trim();
|
||||
let relayArray = [];
|
||||
for (let i = 1; i <= 32; i++) {
|
||||
if (cbRelay(i).is(':checked')) {
|
||||
relayArray.push(i);
|
||||
}
|
||||
}
|
||||
let relay = relayArray.join(';');
|
||||
if (description.length === 0) {
|
||||
alert("Description cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (soundChannel.length === 0) {
|
||||
alert("Sound Channel cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (box.length === 0) {
|
||||
alert("Box cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (relayArray.length === 0) {
|
||||
alert("At least one relay must be selected");
|
||||
return;
|
||||
}
|
||||
let bz = {
|
||||
description: description,
|
||||
SoundChannel: soundChannel,
|
||||
Box: box,
|
||||
Relay: relay
|
||||
};
|
||||
fetchAPI(APIURL_BroadcastZone + "Add", "POST", bz, null, (okdata) => {
|
||||
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||
alert("Success add new broadcast zone: " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error add new broadcast zone: " + errdata.message);
|
||||
});
|
||||
|
||||
$broadcastzonemodal.modal('hide');
|
||||
});
|
||||
$broadcastzonemodal.off('click.broadcastzoneclose').on('click.broadcastzoneclose', '#broadcastzoneclose', function () {
|
||||
$broadcastzonemodal.modal('hide');
|
||||
});
|
||||
});
|
||||
|
||||
$btnRemove.click(() => {
|
||||
if (selectedBroadcastZoneRow) {
|
||||
let cells = selectedBroadcastZoneRow.find('td');
|
||||
/** @type {BroadcastZone} */
|
||||
let bz = {
|
||||
index: cells.eq(0).text(),
|
||||
description: cells.eq(1).text(),
|
||||
SoundChannel: cells.eq(2).text(),
|
||||
Box: cells.eq(3).text(),
|
||||
Relay: cells.eq(4).text()
|
||||
};
|
||||
if (confirm(`Are you sure to delete broadcast zone [${bz.index}] Description=${bz.description}?`)) {
|
||||
fetchAPI(APIURL_BroadcastZone + "DeleteByIndex/" + bz.index, "DELETE", {}, null, (okdata) => {
|
||||
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||
alert("Success delete broadcast zone: " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error delete broadcast zone: " + errdata.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$btnEdit.click(() => {
|
||||
if (selectedBroadcastZoneRow) {
|
||||
let cells = selectedBroadcastZoneRow.find('td');
|
||||
/** @type {BroadcastZone} */
|
||||
let bz = {
|
||||
index: cells.eq(0).text(),
|
||||
description: cells.eq(1).text(),
|
||||
SoundChannel: cells.eq(2).text(),
|
||||
Box: cells.eq(3).text(),
|
||||
Relay: cells.eq(4).text()
|
||||
};
|
||||
if (confirm(`Are you sure to edit broadcast zone [${bz.index}] Description=${bz.description} SoundChannel=${bz.SoundChannel} Box=${bz.Box} Relay=${bz.Relay}?`)) {
|
||||
$broadcastzonemodal.modal('show');
|
||||
clearBroadcastZoneModal();
|
||||
$broadcastzoneindex.val(bz.index);
|
||||
$broadcastzonedescription.val(bz.description);
|
||||
$broadcastzonesoundchannel.val(bz.SoundChannel);
|
||||
$broadcastzonebox.val(bz.Box);
|
||||
if (bz.Relay) {
|
||||
bz.Relay.split(';').forEach(relayId => {
|
||||
let id = parseInt(relayId, 10);
|
||||
cbRelay(id).prop('checked', true);
|
||||
});
|
||||
}
|
||||
$broadcastzonemodal.off('click.broadcastzonesave').on('click.broadcastzonesave', '#broadcastzonesave', function () {
|
||||
let description = $broadcastzonedescription.val().trim();
|
||||
let soundChannel = $broadcastzonesoundchannel.val().trim();
|
||||
let box = $broadcastzonebox.val().trim();
|
||||
let relayArray = [];
|
||||
for (let i = 1; i <= 32; i++) {
|
||||
if (cbRelay(i).is(':checked')) {
|
||||
relayArray.push(i);
|
||||
}
|
||||
}
|
||||
let relay = relayArray.join(';');
|
||||
if (description.length === 0) {
|
||||
alert("Description cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (soundChannel.length === 0) {
|
||||
alert("Sound Channel cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (box.length === 0) {
|
||||
alert("Box cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (relayArray.length === 0) {
|
||||
alert("At least one relay must be selected");
|
||||
return;
|
||||
}
|
||||
let bzUpdate = {
|
||||
description: description,
|
||||
SoundChannel: soundChannel,
|
||||
Box: box,
|
||||
Relay: relay
|
||||
};
|
||||
fetchAPI(APIURL_BroadcastZone + "UpdateByIndex/" + bz.index, "PATCH", bzUpdate, null, (okdata) => {
|
||||
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||
alert("Success edit broadcast zone: " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error edit broadcast zone: " + errdata.message);
|
||||
});
|
||||
$broadcastzonemodal.modal('hide');
|
||||
});
|
||||
$broadcastzonemodal.off('click.broadcastzoneclose').on('click.broadcastzoneclose', '#broadcastzoneclose', function () {
|
||||
$broadcastzonemodal.modal('hide');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$btnExport.click(() => {
|
||||
DoExport(APIURL_BroadcastZone, "broadcastzones.xlsx", {});
|
||||
});
|
||||
|
||||
$btnImport.click(() => {
|
||||
DoImport(APIURL_BroadcastZone, (okdata) => {
|
||||
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||
alert("Success import broadcast zones: " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error importing broadcast zones from XLSX: " + errdata.message);
|
||||
});
|
||||
});
|
||||
});
|
||||
272
html/webpage/assets/js/languagelink.js
Normal file
272
html/webpage/assets/js/languagelink.js
Normal file
@@ -0,0 +1,272 @@
|
||||
/**
|
||||
* @typedef {Object} LanguageBank
|
||||
* @property {number} index
|
||||
* @property {string} tag
|
||||
* @property {string} language
|
||||
*
|
||||
*/
|
||||
|
||||
/** List of Languagebank data loaded from server
|
||||
* @type {LanguageBank[]}
|
||||
*/
|
||||
let languagebankdata = [];
|
||||
/**
|
||||
* Currently selected languagebank row in the table
|
||||
* @type {JQuery<HTMLElement>|null}
|
||||
*/
|
||||
let selectedlanguagerow = null;
|
||||
|
||||
/**
|
||||
* Fill languagebank table body with values
|
||||
* @param {LanguageBank[]} vv values to fill
|
||||
*/
|
||||
function fill_languagebanktablebody(vv) {
|
||||
$('#languagebanktablebody').empty();
|
||||
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||
vv.forEach(item => {
|
||||
const row = `<tr>
|
||||
<td>${item.index}</td>
|
||||
<td>${item.tag}</td>
|
||||
<td>${item.language}</td>
|
||||
</tr>`;
|
||||
$('#languagebanktablebody').append(row);
|
||||
let $addedrow = $('#languagebanktablebody tr:last');
|
||||
$addedrow.click(function () {
|
||||
if (selectedlanguagerow) {
|
||||
selectedlanguagerow.find('td').css('background-color', '');
|
||||
if (selectedlanguagerow.is($(this))) {
|
||||
selectedlanguagerow = null;
|
||||
$('#btnRemove').prop('disabled', true);
|
||||
$('#btnEdit').prop('disabled', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$addedrow.find('td').css('background-color', '#ffeeba');
|
||||
selectedlanguagerow = $addedrow;
|
||||
$('#btnRemove').prop('disabled', false);
|
||||
$('#btnEdit').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
$('#tablesize').text("Table Size: " + vv.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload language bank from server
|
||||
* @param {string} APIURL API URL endpoint, default "LanguageLink/"
|
||||
*/
|
||||
function reloadLanguageBank(APIURL = "LanguageLink/") {
|
||||
languagebankdata = [];
|
||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||
if (Array.isArray(okdata)) {
|
||||
languagebankdata = okdata;
|
||||
selectedlanguagerow = null;
|
||||
fill_languagebanktablebody(languagebankdata);
|
||||
}
|
||||
}, (errdata) => {
|
||||
alert("Error loading languagebank : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log('languagebank.js loaded');
|
||||
|
||||
$('#languagebanktablebody').empty();
|
||||
selectedlanguagerow = null;
|
||||
let $btnClear = $('#btnClear');
|
||||
let $btnAdd = $('#btnAdd');
|
||||
let $btnRemove = $('#btnRemove');
|
||||
let $btnEdit = $('#btnEdit');
|
||||
let $btnExport = $('#btnExport');
|
||||
let $btnImport = $('#btnImport');
|
||||
$btnRemove.prop('disabled', true);
|
||||
$btnEdit.prop('disabled', true);
|
||||
let APIURL = "LanguageLink/";
|
||||
let $findlanguage = $('#findlanguage');
|
||||
let $modal = $('#languagemodal');
|
||||
let $langid = $modal.find('#languagelinkindex');
|
||||
let $langtag = $modal.find('#languagelinktag');
|
||||
let $cbInd = $modal.find('#langId');
|
||||
let $cbLocal = $modal.find('#langLocal');
|
||||
let $cbEn = $modal.find('#langEn');
|
||||
let $cbArb = $modal.find('#langArb');
|
||||
let $cbJap = $modal.find('#langJap');
|
||||
let $cbChi = $modal.find('#langChi');
|
||||
|
||||
function clearLanguageModal() {
|
||||
$langid.prop('disabled', true).val('');
|
||||
$langtag.val('');
|
||||
$cbInd.prop('checked', false);
|
||||
$cbLocal.prop('checked', false);
|
||||
$cbEn.prop('checked', false);
|
||||
$cbArb.prop('checked', false);
|
||||
$cbJap.prop('checked', false);
|
||||
$cbChi.prop('checked', false);
|
||||
}
|
||||
|
||||
$findlanguage.on('input', function () {
|
||||
let searchTerm = $findlanguage.val().toLowerCase();
|
||||
if (searchTerm.length > 0) {
|
||||
selectedlanguagerow = null;
|
||||
let filtered = languagebankdata.filter(item => item.tag.toLowerCase().includes(searchTerm) || item.language.toLowerCase().includes(searchTerm));
|
||||
fill_languagebanktablebody(filtered);
|
||||
} else {
|
||||
selectedlanguagerow = null;
|
||||
fill_languagebanktablebody(languagebankdata);
|
||||
}
|
||||
});
|
||||
|
||||
reloadLanguageBank(APIURL);
|
||||
$btnClear.click(() => {
|
||||
DoClear(APIURL, "LanguageLink", (okdata) => {
|
||||
reloadLanguageBank(APIURL);
|
||||
alert("Success clear languageLink : " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error clear languageLink : " + errdata.message);
|
||||
});
|
||||
|
||||
});
|
||||
$btnAdd.click(() => {
|
||||
// show modal with id 'languagemodal'
|
||||
$modal.modal('show');
|
||||
clearLanguageModal();
|
||||
|
||||
// save button click event
|
||||
$modal.off('click.languagelinksave').on('click.languagelinksave', '#languagelinksave', function () {
|
||||
const tag = $langtag.val();
|
||||
const langs = [];
|
||||
if ($cbInd.is(':checked')) langs.push('INDONESIA');
|
||||
if ($cbLocal.is(':checked')) langs.push('LOCAL');
|
||||
if ($cbEn.is(':checked')) langs.push('ENGLISH');
|
||||
if ($cbArb.is(':checked')) langs.push('ARABIC');
|
||||
if ($cbJap.is(':checked')) langs.push('JAPANESE');
|
||||
if ($cbChi.is(':checked')) langs.push('CHINESE');
|
||||
|
||||
if (tag.length === 0) {
|
||||
alert("Tag cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (langs.length === 0) {
|
||||
alert("At least one language must be selected");
|
||||
return;
|
||||
}
|
||||
|
||||
const langString = langs.join(';');
|
||||
let ll = {
|
||||
tag: tag,
|
||||
language: langString
|
||||
}
|
||||
fetchAPI(APIURL + "Add", "POST", {}, ll, (okdata) => {
|
||||
alert("Success add language : " + okdata.message);
|
||||
reloadLanguageBank(APIURL);
|
||||
}, (errdata) => {
|
||||
alert("Error add language : " + errdata.message);
|
||||
});
|
||||
$modal.modal('hide');
|
||||
|
||||
|
||||
});
|
||||
// close button click event
|
||||
$modal.off('click.languagelinkclose').on('click.languagelinkclose', '#languagelinkclose', function () {
|
||||
$modal.modal('hide');
|
||||
});
|
||||
});
|
||||
$btnRemove.click(() => {
|
||||
if (selectedlanguagerow) {
|
||||
let cells = selectedlanguagerow.find('td');
|
||||
/** @type {Language} */
|
||||
let ll = {
|
||||
index: cells.eq(0).text(),
|
||||
tag: cells.eq(1).text(),
|
||||
language: cells.eq(2).text()
|
||||
}
|
||||
if (confirm(`Are you sure to delete language [${ll.index}] Tag=${ll.tag} Language=${ll.language}?`)) {
|
||||
fetchAPI(APIURL + "DeleteByIndex/" + ll.index, "DELETE", {}, null, (okdata) => {
|
||||
reloadLanguageBank(APIURL);
|
||||
alert("Success delete language : " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error delete language : " + errdata.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
$btnEdit.click(() => {
|
||||
if (selectedlanguagerow) {
|
||||
let cells = selectedlanguagerow.find('td');
|
||||
/** @type {Language} */
|
||||
let ll = {
|
||||
index: cells.eq(0).text(),
|
||||
tag: cells.eq(1).text(),
|
||||
language: cells.eq(2).text()
|
||||
}
|
||||
if (confirm(`Are you sure to edit language [${ll.index}] Tag=${ll.tag} Language=${ll.language}?`)) {
|
||||
|
||||
clearLanguageModal();
|
||||
$langid.val(ll.index);
|
||||
$langtag.val(ll.tag);
|
||||
let langs = ll.language.toUpperCase().split(';');
|
||||
$cbInd.prop('checked', langs.includes('INDONESIA'));
|
||||
$cbLocal.prop('checked', langs.includes('LOCAL'));
|
||||
$cbEn.prop('checked', langs.includes('ENGLISH'));
|
||||
$cbArb.prop('checked', langs.includes('ARABIC'));
|
||||
$cbJap.prop('checked', langs.includes('JAPANESE'));
|
||||
$cbChi.prop('checked', langs.includes('CHINESE'));
|
||||
$modal.modal('show');
|
||||
// save button click event
|
||||
$modal.off('click.languagelinksave').on('click.languagelinksave', '#languagelinksave', function () {
|
||||
const tag = $langtag.val();
|
||||
const langs = [];
|
||||
if ($cbInd.is(':checked')) langs.push('INDONESIA');
|
||||
if ($cbLocal.is(':checked')) langs.push('LOCAL');
|
||||
if ($cbEn.is(':checked')) langs.push('ENGLISH');
|
||||
if ($cbArb.is(':checked')) langs.push('ARABIC');
|
||||
if ($cbJap.is(':checked')) langs.push('JAPANESE');
|
||||
if ($cbChi.is(':checked')) langs.push('CHINESE');
|
||||
if (tag.length === 0) {
|
||||
alert("Tag cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (langs.length === 0) {
|
||||
alert("At least one language must be selected");
|
||||
return;
|
||||
}
|
||||
const langString = langs.join(';');
|
||||
if (ll.tag === tag && ll.language === langString) {
|
||||
alert("No changes detected");
|
||||
$modal.modal('hide');
|
||||
return;
|
||||
}
|
||||
|
||||
ll.tag = tag;
|
||||
ll.language = langString;
|
||||
fetchAPI(APIURL + "UpdateByIndex/" + ll.index, "PATCH", {}, ll, (okdata) => {
|
||||
reloadLanguageBank(APIURL);
|
||||
alert("Success edit language : " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error edit language : " + errdata.message);
|
||||
});
|
||||
|
||||
$modal.modal('hide');
|
||||
});
|
||||
// close button click event
|
||||
$modal.off('click.languagelinkclose').on('click.languagelinkclose', '#languagelinkclose', function () {
|
||||
$modal.modal('hide');
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
$btnExport.click(() => {
|
||||
DoExport(APIURL, "languagebank.xlsx", {});
|
||||
|
||||
});
|
||||
$btnImport.click(() => {
|
||||
DoImport(APIURL, (okdata) => {
|
||||
reloadLanguageBank(APIURL);
|
||||
alert("Success import languagebank : " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error importing languagebank from XLSX : " + errdata.message);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
97
html/webpage/assets/js/log.js
Normal file
97
html/webpage/assets/js/log.js
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* @typedef {Object} Log
|
||||
* @property {number} index
|
||||
* @property {string} datenya
|
||||
* @property {string} timenya
|
||||
* @property {string} machine
|
||||
* @property {string} description
|
||||
*/
|
||||
|
||||
/** List of Log data loaded from server
|
||||
* @type {Log[]}
|
||||
*/
|
||||
let logdata = [];
|
||||
|
||||
/**
|
||||
* Fill log table body with values
|
||||
* @param {Log[]} vv values to fill
|
||||
*/
|
||||
function fill_logtablebody(vv) {
|
||||
$('#logtablebody').empty();
|
||||
$('#btnExport').prop('disabled', true);
|
||||
$('#searchfilter').prop('disabled', true);
|
||||
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||
vv.forEach(item => {
|
||||
const row = `<tr>
|
||||
<td>${item.index}</td>
|
||||
<td>${item.datenya}</td>
|
||||
<td>${item.timenya}</td>
|
||||
<td>${item.machine}</td>
|
||||
<td>${item.description}</td>
|
||||
</tr>`;
|
||||
$('#logtablebody').append(row);
|
||||
});
|
||||
$('#tablesize').text("Table Size: " + vv.length);
|
||||
$('#btnExport').prop('disabled', false);
|
||||
$('#searchfilter').prop('disabled', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload logs from server with date and filter
|
||||
* @param {String} APIURL API URL endpoint , default "Log/"
|
||||
* @param {String} date date in format dd-mm-yyyy
|
||||
* @param {String} filter log filter text
|
||||
*/
|
||||
function reloadLogs(APIURL = "Log/", date, filter) {
|
||||
const params = new URLSearchParams({
|
||||
date: date,
|
||||
filter: filter
|
||||
})
|
||||
fetchAPI(APIURL + "List?" + params.toString(), "GET", {}, null, (okdata) => {
|
||||
if (Array.isArray(okdata)) {
|
||||
logdata = okdata;
|
||||
fill_logtablebody(okdata);
|
||||
}
|
||||
}, (errdata) => {
|
||||
alert("Error loading logs : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log("log.js ready");
|
||||
const $logdate = $('#logdate');
|
||||
const $searchfilter = $('#searchfilter');
|
||||
const $logtable = $('#logtablebody')
|
||||
const $btnExport = $('#btnExport');
|
||||
let selectedlogdate = "";
|
||||
let logfilter = "";
|
||||
let APIURL = "Log/";
|
||||
$logtable.empty();
|
||||
|
||||
|
||||
if (!$logdate.val()) {
|
||||
const today = new Date();
|
||||
const dd = String(today.getDate()).padStart(2, '0');
|
||||
const mm = String(today.getMonth() + 1).padStart(2, '0');
|
||||
const yyyy = today.getFullYear();
|
||||
$logdate.val(`${yyyy}-${mm}-${dd}`);
|
||||
selectedlogdate = `${dd}-${mm}-${yyyy}`;
|
||||
reloadLogs(APIURL, selectedlogdate, logfilter);
|
||||
}
|
||||
$logdate.off('change').on('change', function () {
|
||||
const selected = $(this).val();
|
||||
if (selected) {
|
||||
const [year, month, day] = selected.split('-');
|
||||
selectedlogdate = `${day}-${month}-${year}`;
|
||||
reloadLogs(APIURL, selectedlogdate, logfilter);
|
||||
}
|
||||
});
|
||||
|
||||
$searchfilter.off('input').on('input', function () {
|
||||
logfilter = $(this).val();
|
||||
reloadLogs(APIURL, selectedlogdate, logfilter);
|
||||
});
|
||||
$btnExport.off('click').on('click', function () {
|
||||
DoExport(APIURL, "log.xlsx", { date: selectedlogdate, filter: logfilter });
|
||||
});
|
||||
});
|
||||
452
html/webpage/assets/js/messagebank.js
Normal file
452
html/webpage/assets/js/messagebank.js
Normal file
@@ -0,0 +1,452 @@
|
||||
/**
|
||||
* @typedef {Object} MessageBank
|
||||
* @property {number} index
|
||||
* @property {string} description
|
||||
* @property {string} language
|
||||
* @property {number} aNN_ID
|
||||
* @property {string} voice_Type
|
||||
* @property {string} message_Detail
|
||||
* @property {string} message_TAGS
|
||||
*/
|
||||
|
||||
/**
|
||||
* List of Messagebank data loaded from server
|
||||
* @type {MessageBank[]}
|
||||
*/
|
||||
let messagebankdata = [];
|
||||
/**
|
||||
* Currently selected messagebank row in the table
|
||||
* @type {JQuery<HTMLElement>|null}
|
||||
*/
|
||||
let selectedmessagerow = null;
|
||||
|
||||
/**
|
||||
* Fill messagebank table body with values
|
||||
* @param {MessageBank[]} vv values to fill
|
||||
*/
|
||||
function fill_messagebanktablebody(vv) {
|
||||
$('#messagebanktablebody').empty();
|
||||
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||
vv.forEach(item => {
|
||||
const row = `<tr>
|
||||
<td>${item.index}</td>
|
||||
<td>${item.description}</td>
|
||||
<td>${item.language}</td>
|
||||
<td>${item.aNN_ID}</td>
|
||||
<td>${item.voice_Type}</td>
|
||||
<td>${item.message_Detail}</td>
|
||||
<td>${item.message_TAGS}</td>
|
||||
</tr>`;
|
||||
$('#messagebanktablebody').append(row);
|
||||
let $addedrow = $('#messagebanktablebody tr:last');
|
||||
$addedrow.click(function () {
|
||||
if (selectedmessagerow) {
|
||||
selectedmessagerow.find('td').css('background-color', '');
|
||||
if (selectedmessagerow.is($(this))) {
|
||||
selectedmessagerow = null;
|
||||
$('#btnRemove').prop('disabled', true);
|
||||
$('#btnEdit').prop('disabled', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$addedrow.find('td').css('background-color', '#ffeeba');
|
||||
selectedmessagerow = $addedrow;
|
||||
$('#btnRemove').prop('disabled', false);
|
||||
$('#btnEdit').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#tablesize').text("Table Size: " + vv.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload message bank from server
|
||||
* @param {string} APIURL API URL endpoint, default "MessageBank/"
|
||||
*/
|
||||
function reloadMessageBank(APIURL = "MessageBank/") {
|
||||
messagebankdata = [];
|
||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||
if (Array.isArray(okdata)) {
|
||||
messagebankdata = okdata;
|
||||
selectedmessagerow = null;
|
||||
fill_messagebanktablebody(messagebankdata);
|
||||
}
|
||||
}, (errdata) => {
|
||||
alert("Error loading messagebank : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log("messagebank.js loaded");
|
||||
$('#messagebanktablebody').empty();
|
||||
selectedmessagerow = null;
|
||||
let $btnClear = $('#btnClear');
|
||||
let $btnAdd = $('#btnAdd');
|
||||
let $btnRemove = $('#btnRemove');
|
||||
let $btnEdit = $('#btnEdit');
|
||||
let $btnExport = $('#btnExport');
|
||||
let $btnImport = $('#btnImport');
|
||||
$btnRemove.prop('disabled', true);
|
||||
$btnEdit.prop('disabled', true);
|
||||
let APIURL = "MessageBank/";
|
||||
let $findmessage = $('#findmessage');
|
||||
|
||||
// modal for add / edit messagebank
|
||||
let $modal = $('#messagebankmodal');
|
||||
// text input, disabled by default
|
||||
let $messageindex = $modal.find('#messageindex');
|
||||
// text input
|
||||
let $messagedescription = $modal.find('#messagedescription');
|
||||
// select input, options loaded from languages[]
|
||||
let $messagelanguage = $modal.find('#messagelanguage');
|
||||
// number input from 1 to 100
|
||||
let $messageannid = $modal.find('#messageannid');
|
||||
// select input, options loaded from voiceTypes[]
|
||||
let $messagevoicetype = $modal.find('#messagevoicetype');
|
||||
// list <ul> of available categories and phrases
|
||||
let $messageavailablevariables = $modal.find('#messageavailablevariables');
|
||||
// list <ul> of selected categories and phrases
|
||||
let $messageselectedvariables = $modal.find('#messageselectedvariables');
|
||||
// for clearing messageselectedvariables
|
||||
let $btnclearlist = $modal.find('#btnclearlist');
|
||||
// for removing selected item from messageselectedvariables
|
||||
let $btnremovefromlist = $modal.find('#btnremovefromlist');
|
||||
// for adding selected item from messageavailablevariables to messageselectedvariables
|
||||
let $btnaddtolist = $modal.find('#btnaddtolist');
|
||||
|
||||
/**
|
||||
* Refill messageavailablevariables options from categories[]
|
||||
* and soundbankdata with category "Phrase" if messagelanguage and messagevoicetype are selected
|
||||
*/
|
||||
function refill_messageavailablevariables() {
|
||||
$messageavailablevariables.empty();
|
||||
categories.forEach(cat => {
|
||||
$messageavailablevariables.append(ListItem(`{${cat}}`));
|
||||
});
|
||||
if ($messagelanguage.val() && $messagevoicetype.val()) {
|
||||
soundbankdata
|
||||
.filter(sb => sb.language.toLowerCase() === $messagelanguage.val().toLowerCase())
|
||||
.filter(sb => sb.voiceType.toLowerCase() === $messagevoicetype.val().toLowerCase())
|
||||
.filter(sb => sb.category.toLowerCase() === "phrase")
|
||||
.forEach(sb => {
|
||||
$messageavailablevariables.append(ListItem(`[${sb.Description}]`));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear message modal to default state
|
||||
*/
|
||||
function clearMessageModal() {
|
||||
$messageindex.val('').prop('disabled', true);
|
||||
$messagedescription.val('');
|
||||
// fill messagelanguage options from languages[]
|
||||
$messagelanguage.empty();
|
||||
languages.forEach(lang => {
|
||||
$messagelanguage.append(new Option(lang, lang));
|
||||
});
|
||||
$messagelanguage.val(null);
|
||||
$messagelanguage.on('change', function () {
|
||||
refill_messageavailablevariables();
|
||||
});
|
||||
// set default annid to 1
|
||||
$messageannid.val(1);
|
||||
// fill messagevoicetype options from voiceTypes[]
|
||||
$messagevoicetype.empty();
|
||||
voiceTypes.forEach(vt => {
|
||||
$messagevoicetype.append(new Option(vt, vt));
|
||||
});
|
||||
$messagevoicetype.val(null);
|
||||
$messagevoicetype.on('change', function () {
|
||||
refill_messageavailablevariables();
|
||||
});
|
||||
|
||||
refill_messageavailablevariables();
|
||||
$messageselectedvariables.empty();
|
||||
|
||||
// event on btnclearlist
|
||||
$btnclearlist.off('click').on('click', function () {
|
||||
if ($messageselectedvariables.children().length > 0) {
|
||||
if (confirm("Are you sure want to clear selected variables list?")) {
|
||||
$messageselectedvariables.empty();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// event on btnremovefromlist
|
||||
$btnremovefromlist.off('click').on('click', function () {
|
||||
let $selected = $messageselectedvariables.find('option:selected');
|
||||
if ($selected.length > 0) {
|
||||
$selected.remove();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// event on btnaddtolist
|
||||
$btnaddtolist.off('click').on('click', function () {
|
||||
let $selected = $messageavailablevariables.find('option:selected');
|
||||
if ($selected.length > 0) {
|
||||
$selected.each(function () {
|
||||
$messageselectedvariables.append($(this).clone());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$findmessage.on('input', function () {
|
||||
let searchTerm = $findmessage.val().toLowerCase();
|
||||
if (searchTerm.length > 0) {
|
||||
selectedmessagerow = null;
|
||||
let filtered = messagebankdata.filter(item => item.description.toLowerCase().includes(searchTerm) || item.message_Detail.toLowerCase().includes(searchTerm) || item.message_TAGS.toLowerCase().includes(searchTerm));
|
||||
fill_messagebanktablebody(filtered);
|
||||
} else {
|
||||
selectedmessagerow = null;
|
||||
fill_messagebanktablebody(messagebankdata);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
reloadMessageBank(APIURL);
|
||||
$btnClear.click(() => {
|
||||
DoClear(APIURL, "Messagebank", (okdata) => {
|
||||
reloadMessageBank(APIURL);
|
||||
alert("Success clear messagebank : " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error clear messagebank : " + errdata.message);
|
||||
});
|
||||
|
||||
});
|
||||
$btnAdd.click(() => {
|
||||
|
||||
$modal.modal('show');
|
||||
clearMessageModal();
|
||||
|
||||
// event on Click save button
|
||||
$modal.off('click.messagebanksave').on('click.messagebanksave', '#messagebanksave', function () {
|
||||
let description = $messagedescription.val().trim();
|
||||
let language = $messagelanguage.val();
|
||||
let annid = parseInt($messageannid.val());
|
||||
let voicetype = $messagevoicetype.val();
|
||||
let messagedetail = "";
|
||||
let messagetags = "";
|
||||
|
||||
// iterate messageselectedvariables children
|
||||
$messageselectedvariables.children().each(function () {
|
||||
let val = $(this).text().trim();
|
||||
if (val.length > 0) {
|
||||
if (val.startsWith('[') && val.endsWith(']')) {
|
||||
// categories
|
||||
messagetags += (messagetags.length > 0 ? " " : "") + val;
|
||||
messagedetail += (messagedetail.length > 0 ? " " : "") + val;
|
||||
} else {
|
||||
// phrases
|
||||
// find in soundbankdata by description with specified language and voicetype
|
||||
let sb = soundbankdata
|
||||
.filter(sb => sb.language.toLowerCase() === language.toLowerCase())
|
||||
.filter(sb => sb.voiceType.toLowerCase() === voicetype.toLowerCase())
|
||||
.find(sb => sb.Description.toLowerCase() === val.toLowerCase());
|
||||
if (sb) {
|
||||
messagedetail += (messagedetail.length > 0 ? " " : "") + sb.Description;
|
||||
messagetags += (messagetags.length > 0 ? " " : "") + sb.tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (description.length === 0) {
|
||||
alert("Description cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (!language) {
|
||||
alert("Language cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (isNaN(annid) || annid < 1 || annid > 100) {
|
||||
alert("ANN_ID must be a number between 1 and 100");
|
||||
return;
|
||||
}
|
||||
if (!voicetype) {
|
||||
alert("Voice Type cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (messagedetail.length === 0 || messagetags.length === 0) {
|
||||
alert("Message haven't been constructed, please add categories and phrases");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let mb = {
|
||||
Description: description,
|
||||
Language: language,
|
||||
ANN_ID: annid,
|
||||
Voice_Type: voicetype,
|
||||
Message_Detail: messagedetail,
|
||||
Message_TAGS: messagetags
|
||||
};
|
||||
// send to server using fetchAPI
|
||||
fetchAPI(APIURL + "Add", "POST", mb, null, (okdata) => {
|
||||
reloadMessageBank(APIURL);
|
||||
alert("Success add new messagebank : " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error add new messagebank : " + errdata.message);
|
||||
});
|
||||
|
||||
$modal.modal('hide');
|
||||
});
|
||||
// event on Click close button
|
||||
$modal.off('click.messagebankclose').on('click.messagebankclose', '#messagebankclose', function () {
|
||||
$modal.modal('hide');
|
||||
});
|
||||
});
|
||||
$btnRemove.click(() => {
|
||||
if (selectedmessagerow) {
|
||||
let cells = selectedmessagerow.find('td');
|
||||
/** @type {MessageBank} */
|
||||
let mb = {
|
||||
index: cells.eq(0).text(),
|
||||
description: cells.eq(1).text(),
|
||||
language: cells.eq(2).text(),
|
||||
aNN_ID: parseInt(cells.eq(3).text()),
|
||||
voice_Type: cells.eq(4).text(),
|
||||
message_Detail: cells.eq(5).text(),
|
||||
message_TAGS: cells.eq(6).text()
|
||||
}
|
||||
|
||||
if (confirm(`Are you sure to delete messagebank [${mb.index}] Description=${mb.description}? ANN_ID=${mb.aNN_ID} Language=${mb.language} Voice_Type=${mb.voice_Type} `)) {
|
||||
fetchAPI(APIURL + "DeleteByIndex/" + mb.index, "DELETE", {}, null, (okdata) => {
|
||||
reloadMessageBank(APIURL);
|
||||
alert("Success delete messagebank : " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error delete messagebank : " + errdata.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
$btnEdit.click(() => {
|
||||
if (selectedmessagerow) {
|
||||
let cells = selectedmessagerow.find('td');
|
||||
/** @type {MessageBank} */
|
||||
let mb = {
|
||||
index: cells.eq(0).text(),
|
||||
description: cells.eq(1).text(),
|
||||
language: cells.eq(2).text(),
|
||||
aNN_ID: parseInt(cells.eq(3).text()),
|
||||
voice_Type: cells.eq(4).text(),
|
||||
message_Detail: cells.eq(5).text(),
|
||||
message_TAGS: cells.eq(6).text()
|
||||
}
|
||||
if (confirm(`Are you sure to edit messagebank [${mb.index}] Description=${mb.description} ANN_ID=${mb.aNN_ID} Language=${mb.language} Voice_Type=${mb.voice_Type} `)) {
|
||||
$modal.modal('show');
|
||||
|
||||
clearMessageModal();
|
||||
// Fill modal fields with selected messagebank data
|
||||
$messageindex.val(mb.index).prop('disabled', true);
|
||||
$messagedescription.val(mb.description);
|
||||
// Fill messagelanguage options and select current
|
||||
$messagelanguage.empty();
|
||||
languages.forEach(lang => {
|
||||
$messagelanguage.append(new Option(lang, lang));
|
||||
});
|
||||
$messagelanguage.val(mb.language);
|
||||
// Fill messagevoicetype options and select current
|
||||
$messagevoicetype.empty();
|
||||
voiceTypes.forEach(vt => {
|
||||
$messagevoicetype.append(new Option(vt, vt));
|
||||
});
|
||||
$messagevoicetype.val(mb.voice_Type);
|
||||
// Set annid
|
||||
$messageannid.val(mb.aNN_ID);
|
||||
// Refill message available variables
|
||||
refill_messageavailablevariables();
|
||||
// Fill messageselectedvariables from message_Detail and message_TAGS
|
||||
$messageselectedvariables.empty();
|
||||
if (mb.message_Detail) {
|
||||
mb.message_Detail.split(' ').forEach(val => {
|
||||
$messageselectedvariables.append(ListItem(val));
|
||||
});
|
||||
}
|
||||
|
||||
// Save button event
|
||||
$modal.off('click.messagebanksave').on('click.messagebanksave', '#messagebanksave', function () {
|
||||
let description = $messagedescription.val().trim();
|
||||
let language = $messagelanguage.val();
|
||||
let annid = parseInt($messageannid.val());
|
||||
let voicetype = $messagevoicetype.val();
|
||||
let messagedetail = "";
|
||||
let messagetags = "";
|
||||
$messageselectedvariables.children().each(function () {
|
||||
let val = $(this).text().trim();
|
||||
if (val.length > 0) {
|
||||
if (val.startsWith('[') && val.endsWith(']')) {
|
||||
messagetags += (messagetags.length > 0 ? " " : "") + val;
|
||||
messagedetail += (messagedetail.length > 0 ? " " : "") + val;
|
||||
} else {
|
||||
let sb = soundbankdata
|
||||
.filter(sb => sb.language.toLowerCase() === language.toLowerCase())
|
||||
.filter(sb => sb.voiceType.toLowerCase() === voicetype.toLowerCase())
|
||||
.find(sb => sb.Description && sb.Description.toLowerCase() === val.toLowerCase());
|
||||
if (sb) {
|
||||
messagedetail += (messagedetail.length > 0 ? " " : "") + sb.Description;
|
||||
messagetags += (messagetags.length > 0 ? " " : "") + sb.tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (description.length === 0) {
|
||||
alert("Description cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (!language) {
|
||||
alert("Language cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (isNaN(annid) || annid < 1 || annid > 100) {
|
||||
alert("ANN_ID must be a number between 1 and 100");
|
||||
return;
|
||||
}
|
||||
if (!voicetype) {
|
||||
alert("Voice Type cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (messagedetail.length === 0 || messagetags.length === 0) {
|
||||
alert("Message haven't been constructed, please add categories and phrases");
|
||||
return;
|
||||
}
|
||||
let mbUpdate = {
|
||||
Description: description,
|
||||
Language: language,
|
||||
ANN_ID: annid,
|
||||
Voice_Type: voicetype,
|
||||
Message_Detail: messagedetail,
|
||||
Message_TAGS: messagetags
|
||||
};
|
||||
fetchAPI(APIURL + "UpdateByIndex/" + mb.index, "PATCH", mbUpdate, null, (okdata) => {
|
||||
reloadMessageBank(APIURL);
|
||||
alert("Success edit messagebank : " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error edit messagebank : " + errdata.message);
|
||||
});
|
||||
$modal.modal('hide');
|
||||
});
|
||||
// Close button event
|
||||
$modal.off('click.messagebankclose').on('click.messagebankclose', '#messagebankclose', function () {
|
||||
$modal.modal('hide');
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
$btnExport.click(() => {
|
||||
DoExport(APIURL, "messagebank.xlsx", {});
|
||||
});
|
||||
$btnImport.click(() => {
|
||||
DoImport(APIURL, (okdata) => {
|
||||
reloadMessageBank(APIURL);
|
||||
alert("Success import messagebank : " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error importing messagebank from XLSX : " + errdata.message);
|
||||
});
|
||||
});
|
||||
});
|
||||
405
html/webpage/assets/js/schedulebank.js
Normal file
405
html/webpage/assets/js/schedulebank.js
Normal file
@@ -0,0 +1,405 @@
|
||||
/**
|
||||
* @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[]}
|
||||
*/
|
||||
let schedulebankdata = [];
|
||||
/**
|
||||
* Currently selected schedulebank row in the table
|
||||
* @type {JQuery<HTMLElement>|null}
|
||||
*/
|
||||
let selectedschedulerow = null;
|
||||
|
||||
/**
|
||||
* Fill schedulebank table body with values
|
||||
* @param {ScheduleBank[]} vv values to fill
|
||||
*/
|
||||
function fill_schedulebanktablebody(vv) {
|
||||
$('#schedulebanktablebody').empty();
|
||||
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||
vv.forEach(item => {
|
||||
const row = `<tr>
|
||||
<td>${item.index}</td>
|
||||
<td>${item.description}</td>
|
||||
<td>${item.day}</td>
|
||||
<td>${item.time}</td>
|
||||
<td>${item.soundpath}</td>
|
||||
<td>${item.repeat}</td>
|
||||
<td>${item.enable}</td>
|
||||
<td>${item.broadcastZones}</td>
|
||||
<td>${item.language}</td>
|
||||
</tr>`;
|
||||
$('#schedulebanktablebody').append(row);
|
||||
let $addedrow = $('#schedulebanktablebody tr:last');
|
||||
$addedrow.click(function () {
|
||||
if (selectedschedulerow) {
|
||||
selectedschedulerow.find('td').css('background-color', '');
|
||||
if (selectedschedulerow.is($(this))) {
|
||||
selectedschedulerow = null;
|
||||
$('#btnRemove').prop('disabled', true);
|
||||
$('#btnEdit').prop('disabled', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$addedrow.find('td').css('background-color', '#ffeeba');
|
||||
selectedschedulerow = $addedrow;
|
||||
$('#btnRemove').prop('disabled', false);
|
||||
$('#btnEdit').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
$('#tablesize').text("Table Size: " + vv.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload timer bank from server
|
||||
* @param {string} APIURL API URL endpoint, default "ScheduleBank/"
|
||||
*/
|
||||
function reloadTimerBank(APIURL = "ScheduleBank/") {
|
||||
schedulebankdata = [];
|
||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||
if (Array.isArray(okdata)) {
|
||||
schedulebankdata = okdata;
|
||||
selectedschedulerow = null;
|
||||
fill_schedulebanktablebody(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/";
|
||||
|
||||
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');
|
||||
// text input
|
||||
let $schedulesoundpath = $schedulemodal.find('#schedulesoundpath');
|
||||
// number input 0-5
|
||||
let $schedulerepeat = $schedulemodal.find('#schedulerepeat');
|
||||
// checkbox
|
||||
let $scheduleenable = $schedulemodal.find('#scheduleenable');
|
||||
// div for list of checkboxes
|
||||
let $schedulezones = $schedulemodal.find('#schedulezones');
|
||||
// radio button for everyday
|
||||
let $scheduleeveryday = $schedulemodal.find('#scheduleeveryday');
|
||||
// radio button for weekdays
|
||||
let $schedulesunday = $schedulemodal.find('#schedulesunday');
|
||||
let $schedulemonday = $schedulemodal.find('#schedulemonday');
|
||||
let $scheduletuesday = $schedulemodal.find('#scheduletuesday');
|
||||
let $schedulewednesday = $schedulemodal.find('#schedulewednesday');
|
||||
let $schedulethursday = $schedulemodal.find('#schedulethursday');
|
||||
let $schedulefriday = $schedulemodal.find('#schedulefriday');
|
||||
let $schedulesaturday = $schedulemodal.find('#schedulesaturday');
|
||||
// radio button for specific date
|
||||
let $schedulespecialdate = $schedulemodal.find('#schedulespecialdate');
|
||||
// date input
|
||||
let $scheduledate = $schedulemodal.find('#scheduledate');
|
||||
|
||||
$schedulespecialdate.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');
|
||||
$schedulesoundpath.val('');
|
||||
$schedulerepeat.val('0');
|
||||
$scheduleenable.prop('checked', true);
|
||||
$scheduleeveryday.prop('checked', false);
|
||||
$schedulesunday.prop('checked', false);
|
||||
$schedulemonday.prop('checked', false);
|
||||
$scheduletuesday.prop('checked', false);
|
||||
$schedulewednesday.prop('checked', false);
|
||||
$schedulethursday.prop('checked', false);
|
||||
$schedulefriday.prop('checked', false);
|
||||
$schedulesaturday.prop('checked', false);
|
||||
$schedulespecialdate.prop('checked', false);
|
||||
$scheduledate.prop('disabled', true).val('');
|
||||
|
||||
|
||||
}
|
||||
|
||||
let $findschedule = $('#findschedule');
|
||||
$findschedule.on('input', function () {
|
||||
let searchTerm = $findschedule.val().toLowerCase();
|
||||
if (searchTerm.length > 0) {
|
||||
selectedtimerow = null;
|
||||
let filtered = schedulebankdata.filter(item =>
|
||||
item.description.toLowerCase().includes(searchTerm)
|
||||
|| item.soundpath.toLowerCase().includes(searchTerm)
|
||||
|| item.broadcastZones.toLowerCase().includes(searchTerm));
|
||||
fill_schedulebanktablebody(filtered);
|
||||
} else {
|
||||
selectedtimerow = null;
|
||||
fill_schedulebanktablebody(schedulebankdata);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
reloadTimerBank(APIURL);
|
||||
$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 Soundpath = $schedulesoundpath.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 = $scheduledate.val();
|
||||
} else {
|
||||
|
||||
if ($schedulesunday.is(':checked')) Day = "Sunday";
|
||||
if ($schedulemonday.is(':checked')) Day = "Monday";
|
||||
if ($scheduletuesday.is(':checked')) Day = "Tuesday";
|
||||
if ($schedulewednesday.is(':checked')) Day = "Wednesday";
|
||||
if ($schedulethursday.is(':checked')) Day = "Thursday";
|
||||
if ($schedulefriday.is(':checked')) Day = "Friday";
|
||||
if ($schedulesaturday.is(':checked')) Day = "Saturday";
|
||||
}
|
||||
// Broadcast zones (assuming comma-separated string)
|
||||
const BroadcastZones = $schedulezones.val();
|
||||
|
||||
// Validate required fields
|
||||
if (!Description || !Soundpath || Day === "") {
|
||||
alert("Description, sound path, and day are required.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 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')}`;
|
||||
|
||||
// Prepare object
|
||||
const scheduleObj = {
|
||||
Description,
|
||||
Day,
|
||||
Time,
|
||||
Soundpath,
|
||||
Repeat,
|
||||
Enable,
|
||||
BroadcastZones
|
||||
};
|
||||
|
||||
fetchAPI(APIURL + "Add", "POST", {}, scheduleObj, (okdata) => {
|
||||
alert("Success add schedule: " + okdata.message);
|
||||
reloadTimerBank(APIURL);
|
||||
}, (errdata) => {
|
||||
alert("Error add schedule: " + errdata.message);
|
||||
});
|
||||
|
||||
$schedulemodal.modal('hide');
|
||||
});
|
||||
});
|
||||
$btnRemove.click(() => {
|
||||
if (selectedtimerow) {
|
||||
let cells = selectedtimerow.find('td');
|
||||
/** @type {ScheduleBank} */
|
||||
let sr = {
|
||||
index: cells.eq(0).text(),
|
||||
description: cells.eq(1).text(),
|
||||
day: cells.eq(2).text(),
|
||||
time: cells.eq(3).text(),
|
||||
soundpath: cells.eq(4).text(),
|
||||
repeat: cells.eq(5).text(),
|
||||
enable: cells.eq(6).text(),
|
||||
broadcastZones: cells.eq(7).text(),
|
||||
language: cells.eq(8).text()
|
||||
}
|
||||
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 (selectedtimerow) {
|
||||
let cells = selectedtimerow.find('td');
|
||||
/** @type {ScheduleBank} */
|
||||
let sr = {
|
||||
index: cells.eq(0).text(),
|
||||
description: cells.eq(1).text(),
|
||||
day: cells.eq(2).text(),
|
||||
time: cells.eq(3).text(),
|
||||
soundpath: cells.eq(4).text(),
|
||||
repeat: cells.eq(5).text(),
|
||||
enable: cells.eq(6).text(),
|
||||
broadcastZones: cells.eq(7).text(),
|
||||
language: cells.eq(8).text()
|
||||
}
|
||||
if (confirm(`Are you sure to edit schedule [${sr.index}] Description=${sr.description}?`)) {
|
||||
$schedulemodal.modal('show');
|
||||
// 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());
|
||||
$schedulesoundpath.val(sr.soundpath);
|
||||
$schedulerepeat.val(sr.repeat.toString());
|
||||
$scheduleenable.prop('checked', sr.enable.toLowerCase() === 'true');
|
||||
switch (sr.day) {
|
||||
case 'Everyday':
|
||||
$scheduleeveryday.click();
|
||||
break;
|
||||
case 'Sunday':
|
||||
$schedulesunday.click();
|
||||
break;
|
||||
case 'Monday':
|
||||
$schedulemonday.click();
|
||||
break;
|
||||
case 'Tuesday':
|
||||
$scheduletuesday.click();
|
||||
break;
|
||||
case 'Wednesday':
|
||||
$schedulewednesday.click();
|
||||
break;
|
||||
case 'Thursday':
|
||||
$schedulethursday.click();
|
||||
break;
|
||||
case 'Friday':
|
||||
$schedulefriday.click();
|
||||
break;
|
||||
case 'Saturday':
|
||||
$schedulesaturday.click();
|
||||
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(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 = $schedulesoundpath.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 = $scheduledate.val();
|
||||
} else {
|
||||
if ($schedulesunday.is(':checked')) Day = "Sunday";
|
||||
if ($schedulemonday.is(':checked')) Day = "Monday";
|
||||
if ($scheduletuesday.is(':checked')) Day = "Tuesday";
|
||||
if ($schedulewednesday.is(':checked')) Day = "Wednesday";
|
||||
if ($schedulethursday.is(':checked')) Day = "Thursday";
|
||||
if ($schedulefriday.is(':checked')) Day = "Friday";
|
||||
if ($schedulesaturday.is(':checked')) Day = "Saturday";
|
||||
}
|
||||
// Broadcast zones (assuming comma-separated string)
|
||||
const BroadcastZones = $schedulezones.val();
|
||||
|
||||
// Validate required fields
|
||||
if (!Description || !Soundpath || Day === "") {
|
||||
alert("Description, sound path, and day are required.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 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')}`;
|
||||
|
||||
// Prepare object
|
||||
const scheduleObj = {
|
||||
Description,
|
||||
Day,
|
||||
Time,
|
||||
Soundpath,
|
||||
Repeat,
|
||||
Enable,
|
||||
BroadcastZones
|
||||
};
|
||||
|
||||
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');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
$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);
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
274
html/webpage/assets/js/soundbank.js
Normal file
274
html/webpage/assets/js/soundbank.js
Normal file
@@ -0,0 +1,274 @@
|
||||
/**
|
||||
* @typedef {Object} SoundBank
|
||||
* @property {number} index
|
||||
* @property {string} description
|
||||
* @property {string} tag
|
||||
* @property {string} category
|
||||
* @property {string} language
|
||||
* @property {string} voiceType
|
||||
* @property {string} path
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Select2item
|
||||
* @property {number} id
|
||||
* @property {string} text
|
||||
*/
|
||||
|
||||
/**
|
||||
* List of Soundbank data loaded from server
|
||||
* @type {SoundBank[]}
|
||||
*/
|
||||
let soundbankdata = [];
|
||||
/**
|
||||
* Currently selected soundbank row in the table
|
||||
* @type {JQuery<HTMLElement>|null}
|
||||
*/
|
||||
let selectedsoundrow = null;
|
||||
|
||||
/**
|
||||
* List of sound files in the soundbank directory, that ends with .wav or .mp3
|
||||
* @type {string[]}
|
||||
*/
|
||||
let soundbankfiles = [];
|
||||
|
||||
/**
|
||||
* Select2 data source
|
||||
* See https://select2.org/data-sources/formats
|
||||
* @type {Select2item[]}
|
||||
*/
|
||||
let select2data = [];
|
||||
|
||||
/**
|
||||
* Reload sound bank from server
|
||||
* @param {String} APIURL API URL endpoint, default "SoundBank/"
|
||||
*/
|
||||
function reloadSoundBank(APIURL = "SoundBank/") {
|
||||
soundbankdata = [];
|
||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||
|
||||
if (Array.isArray(okdata)) {
|
||||
soundbankdata = okdata;
|
||||
selectedsoundrow = null;
|
||||
fill_soundbanktablebody(soundbankdata);
|
||||
}
|
||||
}, (errdata) => {
|
||||
alert("Error loading soundbank : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill soundbank table body with values
|
||||
* @param {SoundBank[]} vv values to fill
|
||||
*/
|
||||
function fill_soundbanktablebody(vv) {
|
||||
$('#soundbanktablebody').empty();
|
||||
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||
vv.forEach(item => {
|
||||
const row = `<tr>
|
||||
<td>${item.index}</td>
|
||||
<td>${item.description}</td>
|
||||
<td>${item.tag}</td>
|
||||
<td>${item.category}</td>
|
||||
<td>${item.language}</td>
|
||||
<td>${item.voiceType}</td>
|
||||
<td>${item.path}</td>
|
||||
</tr>`;
|
||||
$('#soundbanktablebody').append(row);
|
||||
let $addedrow = $('#soundbanktablebody tr:last');
|
||||
$addedrow.on('click', function () {
|
||||
if (selectedsoundrow) {
|
||||
selectedsoundrow.find('td').css('background-color', '');
|
||||
if (selectedsoundrow.is($(this))) {
|
||||
selectedsoundrow = null;
|
||||
$('#btnRemove').prop('disabled', true);
|
||||
$('#btnEdit').prop('disabled', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$(this).find('td').css('background-color', '#ffeeba');
|
||||
selectedsoundrow = $(this);
|
||||
$('#btnRemove').prop('disabled', false);
|
||||
$('#btnEdit').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$('#tablesize').text("Table Size: " + vv.length);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reload soundbank files from server
|
||||
* @param {String} APIURL API URL endpoint (default "SoundBank/")
|
||||
*/
|
||||
function reloadSoundbankFiles(APIURL = "SoundBank/") {
|
||||
soundbankfiles = [];
|
||||
fetchAPI(APIURL + "ListFiles", "GET", {}, null, (okdata) => {
|
||||
// okdata is a string contains elements separated by semicolon ;
|
||||
if (Array.isArray(okdata)) {
|
||||
soundbankfiles = okdata.filter(item => item.trim().length > 0);
|
||||
// refill select2data
|
||||
select2data = soundbankfiles.map((item, index) => ({ id: index + 1, text: item }));
|
||||
} else console.log("reloadSoundbankFiles: okdata is not array");
|
||||
}, (errdata) => {
|
||||
alert("Error loading soundbank files : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log("soundbank.js loaded successfully");
|
||||
reloadSoundbankFiles();
|
||||
$('#soundbanktablebody').empty();
|
||||
selectedsoundrow = null;
|
||||
let $btnClear = $('#btnClear');
|
||||
let $btnAdd = $('#btnAdd');
|
||||
let $btnRemove = $('#btnRemove');
|
||||
let $btnEdit = $('#btnEdit');
|
||||
let $btnExport = $('#btnExport');
|
||||
let $btnImport = $('#btnImport');
|
||||
$btnRemove.prop('disabled', true);
|
||||
$btnEdit.prop('disabled', true);
|
||||
let APIURL = "SoundBank/";
|
||||
let $modal = $('#soundbankmodal');
|
||||
let $modalindex = $modal.find('#modalindex');
|
||||
let $modaldescription = $modal.find('#modaldescription');
|
||||
let $modaltag = $modal.find('#modaltag');
|
||||
let $modalcategory = $modal.find('#modalcategory');
|
||||
let $modallanguage = $modal.find('#modallanguage');
|
||||
let $modalvoicetype = $modal.find('#modalvoicetype');
|
||||
let $modalpath = $modal.find('#modalpath');
|
||||
|
||||
/**
|
||||
* Clear soundbank modal inputs
|
||||
*/
|
||||
function clearSoundbankModal() {
|
||||
$modalindex.val('').prop('disabled', true);
|
||||
$modaldescription.val('');
|
||||
$modaltag.val('');
|
||||
// fill modalcategory options from categories[]
|
||||
$modalcategory.empty();
|
||||
categories.forEach(cat => {
|
||||
$modalcategory.append(new Option(cat, cat));
|
||||
});
|
||||
$modalcategory.val(null);
|
||||
// fill modallanguage options from languages[]
|
||||
$modallanguage.empty();
|
||||
languages.forEach(lang => {
|
||||
$modallanguage.append(new Option(lang, lang));
|
||||
});
|
||||
$modallanguage.val(null);
|
||||
// fill modalvoicetype options from voiceTypes[]
|
||||
$modalvoicetype.empty();
|
||||
voiceTypes.forEach(vt => {
|
||||
$modalvoicetype.append(new Option(vt, vt));
|
||||
});
|
||||
$modalvoicetype.val(null);
|
||||
// fill modalpath options from soundbankfiles[]
|
||||
// TODO read https://jeesite.com/front/jquery-select2/4.0/index.htm
|
||||
console.log("select2data has " + select2data.length + " items");
|
||||
$('#modalpath').select2({
|
||||
data: select2data
|
||||
})
|
||||
}
|
||||
|
||||
reloadSoundBank(APIURL);
|
||||
$('#findsoundbank').on('input', function () {
|
||||
let searchTerm = $(this).val().trim().toLowerCase();
|
||||
if (searchTerm.length > 0) {
|
||||
selectedsoundrow = null;
|
||||
let filtered = soundbankdata.filter(item => item.description.toLowerCase().includes(searchTerm) || item.tag.toLowerCase().includes(searchTerm) || item.path.toLowerCase().includes(searchTerm));
|
||||
fill_soundbanktablebody(filtered);
|
||||
} else {
|
||||
selectedsoundrow = null;
|
||||
fill_soundbanktablebody(soundbankdata);
|
||||
}
|
||||
});
|
||||
$btnClear.click(() => {
|
||||
DoClear(APIURL, "Soundbank", (okdata) => {
|
||||
reloadSoundBank(APIURL);
|
||||
alert("Success clear soundbank : " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error clear soundbank : " + errdata.message);
|
||||
});
|
||||
|
||||
});
|
||||
$btnAdd.click(() => {
|
||||
|
||||
$modal.modal('show');
|
||||
clearSoundbankModal();
|
||||
// event on Click save button
|
||||
$modal.off('click.soundbanksave').on('click.soundbanksave', '#soundbanksave', function () {
|
||||
//TODO Add soundbank save process here
|
||||
$modal.modal('hide');
|
||||
});
|
||||
// event on Click close button
|
||||
$modal.off('click.soundbankclose').on('click.soundbankclose', '#soundbankclose', function () {
|
||||
$modal.modal('hide');
|
||||
});
|
||||
});
|
||||
$btnRemove.click(() => {
|
||||
if (selectedsoundrow) {
|
||||
let cells = selectedsoundrow.find('td');
|
||||
/** @type {SoundBank} */
|
||||
let sb = {
|
||||
index: cells.eq(0).text(),
|
||||
description: cells.eq(1).text(),
|
||||
tag: cells.eq(2).text(),
|
||||
category: cells.eq(3).text(),
|
||||
language: cells.eq(4).text(),
|
||||
voiceType: cells.eq(5).text(),
|
||||
path: cells.eq(6).text()
|
||||
}
|
||||
if (confirm(`Are you sure to delete soundbank [${sb.index}] Description=${sb.description} Tag=${sb.tag}?`)) {
|
||||
fetchAPI(APIURL + "DeleteByIndex/" + sb.index, "DELETE", {}, null, (okdata) => {
|
||||
reloadSoundBank(APIURL);
|
||||
alert("Success delete soundbank : " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error delete soundbank : " + errdata.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
$btnEdit.click(() => {
|
||||
if (selectedsoundrow) {
|
||||
let cells = selectedsoundrow.find('td');
|
||||
/** @type {SoundBank} */
|
||||
let sb = {
|
||||
index: cells.eq(0).text(),
|
||||
description: cells.eq(1).text(),
|
||||
tag: cells.eq(2).text(),
|
||||
category: cells.eq(3).text(),
|
||||
language: cells.eq(4).text(),
|
||||
voiceType: cells.eq(5).text(),
|
||||
path: cells.eq(6).text()
|
||||
}
|
||||
if (confirm(`Are you sure to edit soundbank [${sb.index}] Description=${sb.description} Tag=${sb.tag}?`)) {
|
||||
$modal.modal('show');
|
||||
$modal.off('hidden.bs.modal').on('hidden.bs.modal', function () {
|
||||
|
||||
// event on Click save button
|
||||
$modal.off('click.soundbanksave').on('click.soundbanksave', '#soundbanksave', function () {
|
||||
//TODO Add soundbank save process here
|
||||
$modal.modal('hide');
|
||||
});
|
||||
// event on Click close button
|
||||
$modal.off('click.soundbankclose').on('click.soundbankclose', '#soundbankclose', function () {
|
||||
$modal.modal('hide');
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
$btnExport.click(() => {
|
||||
DoExport(APIURL, "soundbank.xlsx", {});
|
||||
});
|
||||
$btnImport.click(() => {
|
||||
DoImport(APIURL, (okdata) => {
|
||||
reloadSoundBank(APIURL);
|
||||
alert("Success import soundbank : " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error importing soundbank from XLSX : " + errdata.message);
|
||||
});
|
||||
});
|
||||
});
|
||||
181
html/webpage/assets/js/soundchannel.js
Normal file
181
html/webpage/assets/js/soundchannel.js
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* @typedef {Object} SoundChannel
|
||||
* @property {number} index - The index of the sound channel.
|
||||
* @property {string} channel - The name of the sound channel.
|
||||
* @property {string} ip - The IP address associated with the sound channel.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {SoundChannel[]}
|
||||
*/
|
||||
let soundChannels = [];
|
||||
|
||||
// Currently selected sound channel row in the table
|
||||
let selectedSoundChannel = null;
|
||||
|
||||
/**
|
||||
* Fills the sound channel table body with the provided data.
|
||||
* @param {SoundChannel[]} vv Sound channel data to populate the table.
|
||||
*/
|
||||
function fill_soundchanneltablebody(vv) {
|
||||
const $tbody = $('#soundchanneltablebody');
|
||||
const $btnEditSoundChannel = $('#btnEditSoundChannel');
|
||||
const $tablesizeSoundChannel = $('#tablesizeSoundChannel');
|
||||
$tbody.empty();
|
||||
$tablesizeSoundChannel.text('Table Length : N/A');
|
||||
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||
|
||||
vv.forEach(item => {
|
||||
const row = `<tr>
|
||||
<td>${item.index}</td>
|
||||
<td>${item.channel}</td>
|
||||
<td>${item.ip}</td>
|
||||
</tr>`;
|
||||
$tbody.append(row);
|
||||
let $addedrow = $('#soundchanneltablebody tr:last');
|
||||
$addedrow.click(function () {
|
||||
if (selectedSoundChannel) {
|
||||
selectedSoundChannel.find('td').css('background-color', '');
|
||||
if (selectedSoundChannel.is($(this))) {
|
||||
selectedSoundChannel = null;
|
||||
$btnEditSoundChannel.prop('disabled', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$(this).find('td').css('background-color', '#ffeeba');
|
||||
selectedSoundChannel = $(this);
|
||||
$btnEditSoundChannel.prop('disabled', false);
|
||||
});
|
||||
});
|
||||
$tablesizeSoundChannel.text("Table Size: " + vv.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload sound channels from server
|
||||
* @param {String} APIURL API URL endpoint (default "SoundChannel/")
|
||||
*/
|
||||
function reloadSoundChannel(APIURL = "SoundChannel/") {
|
||||
SoundChannelList = [];
|
||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||
if (Array.isArray(okdata)) {
|
||||
//console.log("reloadSoundChannel : ", okdata)
|
||||
SoundChannelList = okdata;
|
||||
fill_soundchanneltablebody(SoundChannelList);
|
||||
} else console.log("reloadSoundChannel: okdata is not array");
|
||||
}, (errdata) => {
|
||||
alert("Error loading sound channels : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log("soundchannel.js loaded successfully");
|
||||
let $soundchannelmodal = $('#soundchannelmodal');
|
||||
let $soundchannelindex = $soundchannelmodal.find('#soundchannelindex');
|
||||
let $soundchanneldescription = $soundchannelmodal.find('#soundchanneldescription');
|
||||
let $soundchannelip = $soundchannelmodal.find('#soundchannelip');
|
||||
|
||||
let $btnReinitializeSoundChannel = $('#btnReinitializeSoundChannel');
|
||||
let $btnEditSoundChannel = $('#btnEditSoundChannel');
|
||||
let $btnExportSoundChannel = $('#btnExportSoundChannel');
|
||||
let $btnImportSoundChannel = $('#btnImportSoundChannel');
|
||||
let $findsoundchannel = $('#findsoundchannel');
|
||||
$btnEditSoundChannel.prop('disabled', true);
|
||||
let API_SoundChannel = "SoundChannel/";
|
||||
|
||||
$findsoundchannel.on('input', function () {
|
||||
let searchTerm = $(this).val().toLowerCase();
|
||||
if (searchTerm.length==0){
|
||||
fill_soundchanneltablebody(SoundChannelList);
|
||||
} else {
|
||||
let filteredChannels = SoundChannelList.filter(channel =>
|
||||
channel.index.toString().includes(searchTerm) ||
|
||||
channel.description.toLowerCase().includes(searchTerm) ||
|
||||
channel.ip.toLowerCase().includes(searchTerm)
|
||||
);
|
||||
fill_soundchanneltablebody(filteredChannels);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Clear sound channel modal inputs
|
||||
*/
|
||||
function clearSoundChannelModal() {
|
||||
$soundchannelindex.val('');
|
||||
$soundchanneldescription.val('');
|
||||
$soundchannelip.val('');
|
||||
}
|
||||
|
||||
reloadSoundChannel(API_SoundChannel);
|
||||
$btnReinitializeSoundChannel.click(() => {
|
||||
DoClear(API_SoundChannel, "SoundChannels", (okdata) => {
|
||||
reloadSoundChannel(API_SoundChannel);
|
||||
alert("Success clear sound channels: " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error clear sound channels: " + errdata.message);
|
||||
});
|
||||
});
|
||||
$btnEditSoundChannel.click(() => {
|
||||
if (selectedSoundChannel) {
|
||||
let cells = selectedSoundChannel.find('td');
|
||||
/** @type {SoundChannel} */
|
||||
let sc = {
|
||||
index: parseInt(cells.eq(0).text(), 10),
|
||||
description: cells.eq(1).text(),
|
||||
ip: cells.eq(2).text()
|
||||
};
|
||||
if (confirm(`Are you sure to edit sound channel [${sc.index}] Description=${sc.description} IP=${sc.ip}?`)) {
|
||||
$soundchannelmodal.modal('show');
|
||||
clearSoundChannelModal();
|
||||
$soundchannelindex.val(sc.index).prop('disabled', true);
|
||||
$soundchanneldescription.val(sc.description);
|
||||
$soundchannelip.val(sc.ip);
|
||||
|
||||
// Handle save changes
|
||||
$soundchannelmodal.off('click.soundchannelsave').on('click.soundchannelsave', '#soundchannelsave', function () {
|
||||
let newsc = {
|
||||
index: parseInt($soundchannelindex.val(), 10),
|
||||
description: $soundchanneldescription.val(),
|
||||
ip: $soundchannelip.val()
|
||||
};
|
||||
if (newsc.description.trim().length === 0) {
|
||||
alert("Description cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (newsc.ip.trim().length === 0) {
|
||||
alert("IP cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (newsc.description===sc.description && newsc.ip===sc.ip){
|
||||
alert("No changes detected");
|
||||
return;
|
||||
}
|
||||
|
||||
fetchAPI(API_SoundChannel + "UpdateByIndex/" + newsc.index, "PATCH", newsc, null, (okdata) => {
|
||||
reloadSoundChannel(API_SoundChannel);
|
||||
alert("Success edit sound channel: " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error edit sound channel: " + errdata.message);
|
||||
});
|
||||
$soundchannelmodal.modal('hide');
|
||||
|
||||
|
||||
});
|
||||
$soundchannelmodal.off('click.soundchannelclose').on('click.soundchannelclose', '#soundchannelclose', function () {
|
||||
$soundchannelmodal.modal('hide');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
$btnExportSoundChannel.click(() => {
|
||||
DoExport(API_SoundChannel, "soundchannels.xlsx", {});
|
||||
});
|
||||
|
||||
$btnImportSoundChannel.click(() => {
|
||||
DoImport(API_SoundChannel, (okdata) => {
|
||||
reloadSoundChannel(API_SoundChannel);
|
||||
alert("Success import sound channels: " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error importing sound channels from XLSX: " + errdata.message);
|
||||
});
|
||||
});
|
||||
});
|
||||
289
html/webpage/broadcastzones.html
Normal file
289
html/webpage/broadcastzones.html
Normal file
@@ -0,0 +1,289 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-bs-theme="light" lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>AAS_NewGen_03Sept25</title>
|
||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="row">
|
||||
<div class="col w-100 h-100 pad-header">
|
||||
<h2 style="text-align: center;">Sound Channel and Broadcast Zones</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" role="dialog" tabindex="-1" id="broadcastzonemodal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Add / Edit Broadcast Zones</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<p class="text-add">Index</p>
|
||||
</div>
|
||||
<div class="col"><input class="w-25 form-control input-add" type="text" id="broadcastzoneindex" placeholder="index"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<p class="text-add">Description</p>
|
||||
</div>
|
||||
<div class="col"><input type="text" id="broadcastzonedescription" class="form-control input-add" placeholder="description"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<p class="text-add">Sound Channel</p>
|
||||
</div>
|
||||
<div class="col"><select id="broadcastzonesoundchannel" class="form-control input-add" placeholder="sound channel"></select></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<p class="text-add">Box</p>
|
||||
</div>
|
||||
<div class="col"><input type="text" id="broadcastzonebox" class="form-control input-add" placeholder="Box ID"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<p class="text-add">Relay</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R01"><label class="form-check-label" for="formCheck-1">01</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R02"><label class="form-check-label" for="formCheck-2">02</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R03"><label class="form-check-label" for="formCheck-7">03</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R04"><label class="form-check-label" for="formCheck-6">04</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R05"><label class="form-check-label" for="formCheck-5">05</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R06"><label class="form-check-label" for="formCheck-4">06</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R07"><label class="form-check-label" for="formCheck-3">07</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R08"><label class="form-check-label" for="formCheck-8">08</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R09"><label class="form-check-label" for="formCheck-25">09</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R10"><label class="form-check-label" for="formCheck-26">10</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R11"><label class="form-check-label" for="formCheck-27">11</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R12"><label class="form-check-label" for="formCheck-28">12</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R13"><label class="form-check-label" for="formCheck-29">13</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R14"><label class="form-check-label" for="formCheck-30">14</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R15"><label class="form-check-label" for="formCheck-31">15</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R16"><label class="form-check-label" for="formCheck-32">16</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R17"><label class="form-check-label" for="formCheck-17">17</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R18"><label class="form-check-label" for="formCheck-18">18</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R19"><label class="form-check-label" for="formCheck-19">19</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R20"><label class="form-check-label" for="formCheck-20">20</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R21"><label class="form-check-label" for="formCheck-21">21</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R22"><label class="form-check-label" for="formCheck-22">22</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R23"><label class="form-check-label" for="formCheck-23">23</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R24"><label class="form-check-label" for="formCheck-24">24</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R25"><label class="form-check-label" for="formCheck-9">25</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R26"><label class="form-check-label" for="formCheck-10">26</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R27"><label class="form-check-label" for="formCheck-11">27</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R28"><label class="form-check-label" for="formCheck-12">28</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R29"><label class="form-check-label" for="formCheck-13">29</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R30"><label class="form-check-label" for="formCheck-14">30</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R31"><label class="form-check-label" for="formCheck-15">31</label></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="R32"><label class="form-check-label" for="formCheck-16">32</label></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="broadcastzoneclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="broadcastzonesave" type="button">Save</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="accordion" role="tablist" id="accordion-1">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" role="tab"><button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-1" aria-expanded="true" aria-controls="accordion-1 .item-1">Sound Channel</button></h2>
|
||||
<div class="accordion-collapse collapse show item-1" role="tabpanel" data-bs-parent="#accordion-1">
|
||||
<div class="accordion-body">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-lg-7 col-xl-7"></div>
|
||||
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2 search">
|
||||
<p class="text-add">Search</p>
|
||||
</div>
|
||||
<div class="col-10 col-sm-10 col-md-3 col-lg-3 col-xl-3"><input class="w-100 form-control" type="text" id="findsoundchannel" placeholder="Search keyword" name="findsoundbank"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-add" id="btnReinitializeSoundChannel" type="button">Re-Initialize</button></div>
|
||||
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-edit" id="btnEditSoundChannel" type="button">Edit</button></div>
|
||||
<div class="col-6 col-sm-6 col-md-6 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-import" id="btnExportSoundChannel" type="button">Export</button></div>
|
||||
<div class="col-6 col-sm-6 col-md-6 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-import" id="btnImportSoundChannel" type="button">Import</button></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<p id="tablesizeSoundChannel" class="text-add" style="text-align: center;">Table Length : N/A</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-sm-1">No</th>
|
||||
<th class="col-sm-3">Description</th>
|
||||
<th class="col">IP Address</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="soundchanneltablebody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-2" aria-expanded="false" aria-controls="accordion-1 .item-2">Broadcast Zones</button></h2>
|
||||
<div class="accordion-collapse collapse item-2" role="tabpanel" data-bs-parent="#accordion-1">
|
||||
<div class="accordion-body">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-lg-7 col-xl-7"></div>
|
||||
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2 search">
|
||||
<p class="text-add">Search</p>
|
||||
</div>
|
||||
<div class="col-10 col-sm-10 col-md-3 col-lg-3 col-xl-3"><input class="w-100 form-control" type="text" id="findzone" placeholder="Search keyword" name="findsoundbank"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-add" id="btnClear" type="button">Clear</button></div>
|
||||
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-add" id="btnAdd" type="button">Add</button></div>
|
||||
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-remove" id="btnRemove" type="button">Remove</button></div>
|
||||
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-edit" id="btnEdit" type="button">Edit</button></div>
|
||||
<div class="col-6 col-sm-6 col-md-6 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-import" id="btnExport" type="button">Export</button></div>
|
||||
<div class="col-6 col-sm-6 col-md-6 col-lg-2 col-xl-2"><button class="btn w-100 pad-button btn-round-basic color-import" id="btnImport" type="button">Import</button></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<p id="tablesize" class="text-add" style="text-align: center;">Table Length : N/A</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-sm-1">No</th>
|
||||
<th class="col-sm-2">Description</th>
|
||||
<th class="col-sm-2">SoundChannel</th>
|
||||
<th class="col-sm-2">Box</th>
|
||||
<th class="col">Relay</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="broadcastzonetablebody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" role="dialog" tabindex="-1" id="soundchannelmodal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Edit Sound Channel</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<p>Index</p>
|
||||
</div>
|
||||
<div class="col"><input class="w-25" type="text" id="soundchannelindex" readonly="" placeholder="index"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<p>Description</p>
|
||||
</div>
|
||||
<div class="col"><input class="w-100" type="text" id="soundchanneldescription" placeholder="Description"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<p>IP Address</p>
|
||||
</div>
|
||||
<div class="col"><input class="w-100" type="text" id="soundchannelip" placeholder="IP Address"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer"><button class="btn btn-light" id="soundchannelclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-primary" id="soundchannelsave" type="button">Save</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/broadcastzones.js"></script>
|
||||
<script src="assets/js/soundchannel.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>AAS_NewGen_03Sept25</title>
|
||||
<title>AAS NewGeneration 17092025</title>
|
||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
@@ -48,6 +48,12 @@
|
||||
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"></path>
|
||||
<path d="M12.5 7H11v6l5.25 3.15.75-1.23-4.5-2.67z"></path>
|
||||
</svg> Timer</a></li>
|
||||
<li class="nav-item .icon-menu"><a class="nav-link link-body-emphasis text-menu" id="broadcastzonelink" href="#"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" class="me-2 icon-menu" style="font-size: 20px;">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M18.2 1H9.8C8.81 1 8 1.81 8 2.8v14.4c0 .99.81 1.79 1.8 1.79l8.4.01c.99 0 1.8-.81 1.8-1.8V2.8c0-.99-.81-1.8-1.8-1.8zM14 3c1.1 0 2 .89 2 2s-.9 2-2 2-2-.89-2-2 .9-2 2-2zm0 13.5c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"></path>
|
||||
<circle cx="14" cy="12.5" r="2.5"></circle>
|
||||
<path d="M6 5H4v16c0 1.1.89 2 2 2h10v-2H6V5z"></path>
|
||||
</svg> Broadcast Zones</a></li>
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="loglink" href="#"><svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" class="icon-menu" style="font-size: 20px;">
|
||||
<g>
|
||||
<rect fill="none" height="24" width="24"></rect>
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<div class="col-4 col-sm-3">
|
||||
<p class="text-add">Index</p>
|
||||
</div>
|
||||
<div class="col"><input type="text" id="languagelinkindex" class="form-control input-add"></div>
|
||||
<div class="col"><input class="w-25 form-control input-add" type="text" id="languagelinkindex" readonly=""></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
@@ -88,6 +88,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/languagelink.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -49,6 +49,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/log.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>AAS_NewGen_03Sept25</title>
|
||||
<title>AAS NewGeneration 17092025</title>
|
||||
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<div class="col-4 col-sm-3">
|
||||
<p class="text-add">Index</p>
|
||||
</div>
|
||||
<div class="col"><input type="number" id="messageindex" class="input-add form-control" placeholder="Index"></div>
|
||||
<div class="col"><input class="w-25 input-add form-control" type="text" id="messageindex" placeholder="Index" readonly=""></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
@@ -130,6 +130,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/messagebank.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -65,7 +65,7 @@
|
||||
<div class="col-4 col-sm-3">
|
||||
<p class="text-add">Index</p>
|
||||
</div>
|
||||
<div class="col"><input type="text" id="modalindex" class="input-add form-control"></div>
|
||||
<div class="col"><input class="w-25 input-add form-control" type="text" id="modalindex" readonly=""></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
@@ -109,6 +109,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/soundbank.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -66,7 +66,7 @@
|
||||
<div class="col-4 col-sm-3">
|
||||
<p class="text-add">Index</p>
|
||||
</div>
|
||||
<div class="col"><input type="text" id="scheduleid" class="input-add form-control" disabled=""></div>
|
||||
<div class="col"><input class="w-25 input-add form-control" type="text" id="scheduleid" readonly=""></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
@@ -174,6 +174,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/schedulebank.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user