Compare commits
7 Commits
54775641bb
...
cf69c72f3c
| Author | SHA1 | Date | |
|---|---|---|---|
| cf69c72f3c | |||
| 20dbc12b02 | |||
| 3768f4263b | |||
| 1e7adeba25 | |||
|
|
83a6ee9fd0 | ||
|
|
e0f3ac2094 | ||
| c55db5e4f7 |
@@ -60,3 +60,20 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pad-relay {
|
||||
padding-left: 1.5rem;
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.pad-time {
|
||||
margin: 0.7rem auto;
|
||||
}
|
||||
|
||||
.pad-day {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.class100 {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
body {
|
||||
background-color: #f8f9fd;
|
||||
/*background-color: #edf1fb;*/
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -13,8 +14,9 @@ body {
|
||||
}
|
||||
|
||||
.search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/*display: flex;*/
|
||||
/*align-items: center;*/
|
||||
margin-top: -0.2rem;
|
||||
}
|
||||
|
||||
.text-header {
|
||||
@@ -188,3 +190,68 @@ nav-item:focus {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.pad-card {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.pad-search {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.pad-row-search {
|
||||
margin-bottom: 0.7rem;
|
||||
}
|
||||
|
||||
.bg-heading1 {
|
||||
background-color: #c6d8ee;
|
||||
color: #2d3578;
|
||||
}
|
||||
|
||||
.bg-heading2 {
|
||||
background-color: #dce5f4;
|
||||
color: #2d3578;
|
||||
}
|
||||
|
||||
.bg-heading3 {
|
||||
background-color: #e9ecf8;
|
||||
color: #2d3578;
|
||||
}
|
||||
|
||||
.accordion-item {
|
||||
/*background: rgba(255, 255, 255, 0.55);*/
|
||||
/*backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);*/
|
||||
/*border-radius: 16px;*/
|
||||
/*box-shadow: 8px 8px 16px rgba(0, 0, 0, 0.12), -8px -8px 16px rgba(255, 255, 255, 0.6);*/
|
||||
/*transition: all 0.3s ease;*/
|
||||
}
|
||||
|
||||
.accordion-item.active {
|
||||
/*background: rgba(45, 53, 120, 0.15);*/
|
||||
/*box-shadow: inset 4px 4px 10px rgba(45, 53, 120, 0.25), inset -4px -4px 10px rgba(255, 255, 255, 0.8);*/
|
||||
}
|
||||
|
||||
.bg-accordion {
|
||||
border: white 2px;
|
||||
/*border-radius: 10px;*/
|
||||
background: #f8f9fd;
|
||||
}
|
||||
|
||||
.card-channel {
|
||||
border-radius: 12px;
|
||||
background: #ffffff;
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
border: #ffffff solid 2px !important;
|
||||
/*padding-left: 2px;*/
|
||||
/*padding-right: 5px;*/
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.pad-accordion {
|
||||
border-radius: 40px;
|
||||
border: white solid 3px;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,10 @@
|
||||
/**
|
||||
* @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;
|
||||
window.selectedBroadcastZoneRow = null;
|
||||
|
||||
/**
|
||||
* Fill broadcast zone table body with values
|
||||
@@ -31,23 +18,23 @@ function fill_broadcastzonetablebody(vv) {
|
||||
<td>${item.index}</td>
|
||||
<td>${item.description}</td>
|
||||
<td>${item.soundChannel}</td>
|
||||
<td>${item.box}</td>
|
||||
<td>${item.relay}</td>
|
||||
<td>${item.id}</td>
|
||||
<td>${item.bp}</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;
|
||||
if (window.selectedBroadcastZoneRow) {
|
||||
window.selectedBroadcastZoneRow.find('td').css('background-color', '');
|
||||
if (window.selectedBroadcastZoneRow.is($(this))) {
|
||||
window.selectedBroadcastZoneRow = null;
|
||||
$('#btnRemove').prop('disabled', true);
|
||||
$('#btnEdit').prop('disabled', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$(this).find('td').css('background-color', '#ffeeba');
|
||||
selectedBroadcastZoneRow = $(this);
|
||||
window.selectedBroadcastZoneRow = $(this);
|
||||
$('#btnRemove').prop('disabled', false);
|
||||
$('#btnEdit').prop('disabled', false);
|
||||
});
|
||||
@@ -55,26 +42,11 @@ function fill_broadcastzonetablebody(vv) {
|
||||
$('#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;
|
||||
window.selectedBroadcastZoneRow = null;
|
||||
let $btnClear = $('#btnClear');
|
||||
let $btnAdd = $('#btnAdd');
|
||||
let $btnEdit = $('#btnEdit');
|
||||
@@ -98,12 +70,12 @@ $(document).ready(function () {
|
||||
$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));
|
||||
window.selectedBroadcastZoneRow = null;
|
||||
let filtered = window.BroadcastZoneList.filter(item => item.description.toLowerCase().includes(searchTerm) || item.id.toLowerCase().includes(searchTerm) || item.soundChannel.toLowerCase().includes(searchTerm) || item.bp.toLowerCase().includes(searchTerm));
|
||||
fill_broadcastzonetablebody(filtered);
|
||||
} else {
|
||||
selectedBroadcastZoneRow = null;
|
||||
fill_broadcastzonetablebody(broadcastzonedata);
|
||||
window.selectedBroadcastZoneRow = null;
|
||||
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -140,11 +112,14 @@ $(document).ready(function () {
|
||||
}
|
||||
}
|
||||
|
||||
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||
reloadBroadcastZones(APIURL_BroadcastZone, () => {
|
||||
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
||||
});
|
||||
|
||||
$btnClear.click(() => {
|
||||
DoClear(APIURL_BroadcastZone, "BroadcastZones", (okdata) => {
|
||||
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
||||
alert("Success clear broadcast zones: " + okdata.message);
|
||||
}, (errdata) => {
|
||||
alert("Error clear broadcast zones: " + errdata.message);
|
||||
@@ -190,8 +165,10 @@ $(document).ready(function () {
|
||||
Relay: relay
|
||||
};
|
||||
fetchAPI(APIURL_BroadcastZone + "Add", "POST", {}, bz, (okdata) => {
|
||||
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||
alert("Success add new broadcast zone: " + okdata.message);
|
||||
reloadBroadcastZones(APIURL_BroadcastZone, () => {
|
||||
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
||||
alert("Success add new broadcast zone: " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error add new broadcast zone: " + errdata.message);
|
||||
});
|
||||
@@ -204,8 +181,8 @@ $(document).ready(function () {
|
||||
});
|
||||
|
||||
$btnRemove.click(() => {
|
||||
if (selectedBroadcastZoneRow) {
|
||||
let cells = selectedBroadcastZoneRow.find('td');
|
||||
if (window.selectedBroadcastZoneRow) {
|
||||
let cells = window.selectedBroadcastZoneRow.find('td');
|
||||
/** @type {BroadcastZone} */
|
||||
let bz = {
|
||||
index: cells.eq(0).text(),
|
||||
@@ -216,8 +193,10 @@ $(document).ready(function () {
|
||||
};
|
||||
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);
|
||||
reloadBroadcastZones(APIURL_BroadcastZone, () => {
|
||||
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
||||
alert("Success delete broadcast zone: " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error delete broadcast zone: " + errdata.message);
|
||||
});
|
||||
@@ -226,8 +205,8 @@ $(document).ready(function () {
|
||||
});
|
||||
|
||||
$btnEdit.click(() => {
|
||||
if (selectedBroadcastZoneRow) {
|
||||
let cells = selectedBroadcastZoneRow.find('td');
|
||||
if (window.selectedBroadcastZoneRow) {
|
||||
let cells = window.selectedBroadcastZoneRow.find('td');
|
||||
/** @type {BroadcastZone} */
|
||||
let bz = {
|
||||
index: cells.eq(0).text(),
|
||||
@@ -236,15 +215,15 @@ $(document).ready(function () {
|
||||
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}?`)) {
|
||||
if (confirm(`Are you sure to edit broadcast zone [${bz.index}] Description=${bz.description} SoundChannel=${bz.SoundChannel} Box=${bz.id} Relay=${bz.bp}?`)) {
|
||||
$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 => {
|
||||
$broadcastzonebox.val(bz.id);
|
||||
if (bz.bp) {
|
||||
bz.bp.split(';').forEach(relayId => {
|
||||
let id = parseInt(relayId, 10);
|
||||
cbRelay(id).prop('checked', true);
|
||||
});
|
||||
@@ -283,8 +262,10 @@ $(document).ready(function () {
|
||||
Relay: relay
|
||||
};
|
||||
fetchAPI(APIURL_BroadcastZone + "UpdateByIndex/" + bz.index, "PATCH", {}, bzUpdate, (okdata) => {
|
||||
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||
alert("Success edit broadcast zone: " + okdata.message);
|
||||
reloadBroadcastZones(APIURL_BroadcastZone, () => {
|
||||
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
||||
alert("Success edit broadcast zone: " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error edit broadcast zone: " + errdata.message);
|
||||
});
|
||||
@@ -303,8 +284,10 @@ $(document).ready(function () {
|
||||
|
||||
$btnImport.click(() => {
|
||||
DoImport(APIURL_BroadcastZone, (okdata) => {
|
||||
reloadBroadcastZones(APIURL_BroadcastZone);
|
||||
alert("Success import broadcast zones: " + okdata.message);
|
||||
reloadBroadcastZones(APIURL_BroadcastZone, () => {
|
||||
fill_broadcastzonetablebody(window.BroadcastZoneList);
|
||||
alert("Success import broadcast zones: " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error importing broadcast zones from XLSX: " + errdata.message);
|
||||
});
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
/**
|
||||
* @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;
|
||||
window.selectedlanguagerow = null;
|
||||
|
||||
/**
|
||||
* Fill languagebank table body with values
|
||||
@@ -32,17 +21,17 @@ function fill_languagebanktablebody(vv) {
|
||||
$('#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;
|
||||
if (window.selectedlanguagerow) {
|
||||
window.selectedlanguagerow.find('td').css('background-color', '');
|
||||
if (window.selectedlanguagerow.is($(this))) {
|
||||
window.selectedlanguagerow = null;
|
||||
$('#btnRemove').prop('disabled', true);
|
||||
$('#btnEdit').prop('disabled', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$addedrow.find('td').css('background-color', '#ffeeba');
|
||||
selectedlanguagerow = $addedrow;
|
||||
window.selectedlanguagerow = $addedrow;
|
||||
$('#btnRemove').prop('disabled', false);
|
||||
$('#btnEdit').prop('disabled', false);
|
||||
});
|
||||
@@ -50,28 +39,13 @@ function fill_languagebanktablebody(vv) {
|
||||
$('#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;
|
||||
window.selectedlanguagerow = null;
|
||||
let $btnClear = $('#btnClear');
|
||||
let $btnAdd = $('#btnAdd');
|
||||
let $btnRemove = $('#btnRemove');
|
||||
@@ -106,20 +80,24 @@ $(document).ready(function () {
|
||||
$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));
|
||||
window.selectedlanguagerow = null;
|
||||
let filtered = window.languagebankdata.filter(item => item.tag.toLowerCase().includes(searchTerm) || item.language.toLowerCase().includes(searchTerm));
|
||||
fill_languagebanktablebody(filtered);
|
||||
} else {
|
||||
selectedlanguagerow = null;
|
||||
fill_languagebanktablebody(languagebankdata);
|
||||
window.selectedlanguagerow = null;
|
||||
fill_languagebanktablebody(window.languagebankdata);
|
||||
}
|
||||
});
|
||||
|
||||
reloadLanguageBank(APIURL);
|
||||
reloadLanguageBank(APIURL, () => {
|
||||
fill_languagebanktablebody(window.languagebankdata);
|
||||
});
|
||||
$btnClear.click(() => {
|
||||
DoClear(APIURL, "LanguageLink", (okdata) => {
|
||||
reloadLanguageBank(APIURL);
|
||||
alert("Success clear languageLink : " + okdata.message);
|
||||
reloadLanguageBank(APIURL, () => {
|
||||
fill_languagebanktablebody(window.languagebankdata);
|
||||
alert("Success clear languageLink : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error clear languageLink : " + errdata.message);
|
||||
});
|
||||
@@ -156,8 +134,10 @@ $(document).ready(function () {
|
||||
language: langString
|
||||
}
|
||||
fetchAPI(APIURL + "Add", "POST", {}, ll, (okdata) => {
|
||||
alert("Success add language : " + okdata.message);
|
||||
reloadLanguageBank(APIURL);
|
||||
reloadLanguageBank(APIURL, () => {
|
||||
fill_languagebanktablebody(window.languagebankdata);
|
||||
alert("Success add language : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error add language : " + errdata.message);
|
||||
});
|
||||
@@ -171,8 +151,8 @@ $(document).ready(function () {
|
||||
});
|
||||
});
|
||||
$btnRemove.click(() => {
|
||||
if (selectedlanguagerow) {
|
||||
let cells = selectedlanguagerow.find('td');
|
||||
if (window.selectedlanguagerow) {
|
||||
let cells = window.selectedlanguagerow.find('td');
|
||||
/** @type {Language} */
|
||||
let ll = {
|
||||
index: cells.eq(0).text(),
|
||||
@@ -181,8 +161,10 @@ $(document).ready(function () {
|
||||
}
|
||||
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);
|
||||
reloadLanguageBank(APIURL, () => {
|
||||
fill_languagebanktablebody(window.languagebankdata);
|
||||
alert("Success delete language : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error delete language : " + errdata.message);
|
||||
});
|
||||
@@ -190,8 +172,8 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
$btnEdit.click(() => {
|
||||
if (selectedlanguagerow) {
|
||||
let cells = selectedlanguagerow.find('td');
|
||||
if (window.selectedlanguagerow) {
|
||||
let cells = window.selectedlanguagerow.find('td');
|
||||
/** @type {Language} */
|
||||
let ll = {
|
||||
index: cells.eq(0).text(),
|
||||
@@ -239,8 +221,10 @@ $(document).ready(function () {
|
||||
ll.tag = tag;
|
||||
ll.language = langString;
|
||||
fetchAPI(APIURL + "UpdateByIndex/" + ll.index, "PATCH", {}, ll, (okdata) => {
|
||||
reloadLanguageBank(APIURL);
|
||||
alert("Success edit language : " + okdata.message);
|
||||
reloadLanguageBank(APIURL, () => {
|
||||
fill_languagebanktablebody(window.languagebankdata);
|
||||
alert("Success edit language : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error edit language : " + errdata.message);
|
||||
});
|
||||
@@ -262,8 +246,10 @@ $(document).ready(function () {
|
||||
});
|
||||
$btnImport.click(() => {
|
||||
DoImport(APIURL, (okdata) => {
|
||||
reloadLanguageBank(APIURL);
|
||||
alert("Success import languagebank : " + okdata.message);
|
||||
reloadLanguageBank(APIURL, () => {
|
||||
fill_languagebanktablebody(window.languagebankdata);
|
||||
alert("Success import languagebank : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error importing languagebank from XLSX : " + errdata.message);
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/** List of Log data loaded from server
|
||||
* @type {Log[]}
|
||||
*/
|
||||
let logdata = [];
|
||||
window.logdata = [];
|
||||
|
||||
/**
|
||||
* Fill log table body with values
|
||||
@@ -18,9 +18,11 @@ let logdata = [];
|
||||
*/
|
||||
function fill_logtablebody(vv) {
|
||||
$('#logtablebody').empty();
|
||||
$('#btnExport').prop('disabled', true);
|
||||
$('#searchfilter').prop('disabled', true);
|
||||
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||
|
||||
if (!Array.isArray(vv) || vv.length === 0) {
|
||||
$('#btnExport').prop('disabled', true);
|
||||
return;
|
||||
}
|
||||
vv.forEach(item => {
|
||||
const row = `<tr>
|
||||
<td>${item.index}</td>
|
||||
@@ -33,7 +35,6 @@ function fill_logtablebody(vv) {
|
||||
});
|
||||
$('#tablesize').text("Table Size: " + vv.length);
|
||||
$('#btnExport').prop('disabled', false);
|
||||
$('#searchfilter').prop('disabled', false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,10 +48,11 @@ function reloadLogs(APIURL = "Log/", date, filter) {
|
||||
date: date,
|
||||
filter: filter
|
||||
})
|
||||
window.logdata = [];
|
||||
fetchAPI(APIURL + "List?" + params.toString(), "GET", {}, null, (okdata) => {
|
||||
if (Array.isArray(okdata)) {
|
||||
logdata = okdata;
|
||||
fill_logtablebody(okdata);
|
||||
window.logdata.push(...okdata);
|
||||
fill_logtablebody(window.logdata);
|
||||
}
|
||||
}, (errdata) => {
|
||||
alert("Error loading logs : " + errdata.message);
|
||||
@@ -59,26 +61,22 @@ function reloadLogs(APIURL = "Log/", date, filter) {
|
||||
|
||||
$(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();
|
||||
$('#logtablebody').empty();
|
||||
|
||||
|
||||
if (!$logdate.val()) {
|
||||
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}`);
|
||||
$('#logdate').val(`${yyyy}-${mm}-${dd}`);
|
||||
selectedlogdate = `${dd}-${mm}-${yyyy}`;
|
||||
reloadLogs(APIURL, selectedlogdate, logfilter);
|
||||
}
|
||||
$logdate.off('change').on('change', function () {
|
||||
$('#logdate').off('change').on('change', function () {
|
||||
const selected = $(this).val();
|
||||
if (selected) {
|
||||
const [year, month, day] = selected.split('-');
|
||||
@@ -87,11 +85,11 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
|
||||
$searchfilter.off('input').on('input', function () {
|
||||
$('#searchfilter').off('input').on('input', function () {
|
||||
logfilter = $(this).val();
|
||||
reloadLogs(APIURL, selectedlogdate, logfilter);
|
||||
});
|
||||
$btnExport.off('click').on('click', function () {
|
||||
$('#btnExport').off('click').on('click', function () {
|
||||
DoExport(APIURL, "log.xlsx", { date: selectedlogdate, filter: logfilter });
|
||||
});
|
||||
});
|
||||
@@ -1,24 +1,9 @@
|
||||
/**
|
||||
* @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;
|
||||
window.selectedmessagerow = null;
|
||||
|
||||
/**
|
||||
* Fill messagebank table body with values
|
||||
@@ -40,17 +25,17 @@ function fill_messagebanktablebody(vv) {
|
||||
$('#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;
|
||||
if (window.selectedmessagerow) {
|
||||
window.selectedmessagerow.find('td').css('background-color', '');
|
||||
if (window.selectedmessagerow.is($(this))) {
|
||||
window.selectedmessagerow = null;
|
||||
$('#btnRemove').prop('disabled', true);
|
||||
$('#btnEdit').prop('disabled', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$addedrow.find('td').css('background-color', '#ffeeba');
|
||||
selectedmessagerow = $addedrow;
|
||||
window.selectedmessagerow = $addedrow;
|
||||
$('#btnRemove').prop('disabled', false);
|
||||
$('#btnEdit').prop('disabled', false);
|
||||
});
|
||||
@@ -59,27 +44,12 @@ function fill_messagebanktablebody(vv) {
|
||||
$('#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;
|
||||
window.selectedmessagerow = null;
|
||||
let $btnClear = $('#btnClear');
|
||||
let $btnAdd = $('#btnAdd');
|
||||
let $btnRemove = $('#btnRemove');
|
||||
@@ -198,21 +168,25 @@ $(document).ready(function () {
|
||||
$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));
|
||||
window.selectedmessagerow = null;
|
||||
let filtered = window.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);
|
||||
window.selectedmessagerow = null;
|
||||
fill_messagebanktablebody(window.messagebankdata);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
reloadMessageBank(APIURL);
|
||||
reloadMessageBank(APIURL, () => {
|
||||
fill_messagebanktablebody(window.messagebankdata);
|
||||
});
|
||||
$btnClear.click(() => {
|
||||
DoClear(APIURL, "Messagebank", (okdata) => {
|
||||
reloadMessageBank(APIURL);
|
||||
alert("Success clear messagebank : " + okdata.message);
|
||||
reloadMessageBank(APIURL, () => {
|
||||
fill_messagebanktablebody(window.messagebankdata);
|
||||
alert("Success clear messagebank : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error clear messagebank : " + errdata.message);
|
||||
});
|
||||
@@ -287,8 +261,10 @@ $(document).ready(function () {
|
||||
};
|
||||
// send to server using fetchAPI
|
||||
fetchAPI(APIURL + "Add", "POST", mb, null, (okdata) => {
|
||||
reloadMessageBank(APIURL);
|
||||
alert("Success add new messagebank : " + okdata.message);
|
||||
reloadMessageBank(APIURL, () => {
|
||||
fill_messagebanktablebody(window.messagebankdata);
|
||||
alert("Success add new messagebank : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error add new messagebank : " + errdata.message);
|
||||
});
|
||||
@@ -301,8 +277,8 @@ $(document).ready(function () {
|
||||
});
|
||||
});
|
||||
$btnRemove.click(() => {
|
||||
if (selectedmessagerow) {
|
||||
let cells = selectedmessagerow.find('td');
|
||||
if (window.selectedmessagerow) {
|
||||
let cells = window.selectedmessagerow.find('td');
|
||||
/** @type {MessageBank} */
|
||||
let mb = {
|
||||
index: cells.eq(0).text(),
|
||||
@@ -316,8 +292,10 @@ $(document).ready(function () {
|
||||
|
||||
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);
|
||||
reloadMessageBank(APIURL, () => {
|
||||
fill_messagebanktablebody(window.messagebankdata);
|
||||
alert("Success delete messagebank : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error delete messagebank : " + errdata.message);
|
||||
});
|
||||
@@ -325,8 +303,8 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
$btnEdit.click(() => {
|
||||
if (selectedmessagerow) {
|
||||
let cells = selectedmessagerow.find('td');
|
||||
if (window.selectedmessagerow) {
|
||||
let cells = window.selectedmessagerow.find('td');
|
||||
/** @type {MessageBank} */
|
||||
let mb = {
|
||||
index: cells.eq(0).text(),
|
||||
@@ -423,8 +401,10 @@ $(document).ready(function () {
|
||||
Message_TAGS: messagetags
|
||||
};
|
||||
fetchAPI(APIURL + "UpdateByIndex/" + mb.index, "PATCH", mbUpdate, null, (okdata) => {
|
||||
reloadMessageBank(APIURL);
|
||||
alert("Success edit messagebank : " + okdata.message);
|
||||
reloadMessageBank(APIURL, () => {
|
||||
fill_messagebanktablebody(window.messagebankdata);
|
||||
alert("Success edit messagebank : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error edit messagebank : " + errdata.message);
|
||||
});
|
||||
@@ -443,8 +423,11 @@ $(document).ready(function () {
|
||||
});
|
||||
$btnImport.click(() => {
|
||||
DoImport(APIURL, (okdata) => {
|
||||
reloadMessageBank(APIURL);
|
||||
alert("Success import messagebank : " + okdata.message);
|
||||
reloadMessageBank(APIURL, () => {
|
||||
fill_messagebanktablebody(window.messagebankdata);
|
||||
alert("Success import messagebank : " + okdata.message);
|
||||
});
|
||||
|
||||
}, (errdata) => {
|
||||
alert("Error importing messagebank from XLSX : " + errdata.message);
|
||||
});
|
||||
|
||||
@@ -1,25 +1,9 @@
|
||||
/**
|
||||
* @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;
|
||||
window.selectedschedulerow = null;
|
||||
|
||||
/**
|
||||
* Fill schedulebank table body with values
|
||||
@@ -61,22 +45,7 @@ function fill_schedulebanktablebody(vv) {
|
||||
$('#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");
|
||||
@@ -101,7 +70,7 @@ $(document).ready(function () {
|
||||
let $schedulehour = $schedulemodal.find('#schedulehour');
|
||||
// number input 0-59
|
||||
let $scheduleminute = $schedulemodal.find('#scheduleminute');
|
||||
// text input
|
||||
// select for messagebank
|
||||
let $schedulesoundpath = $schedulemodal.find('#schedulesoundpath');
|
||||
// number input 0-5
|
||||
let $schedulerepeat = $schedulemodal.find('#schedulerepeat');
|
||||
@@ -137,8 +106,17 @@ $(document).ready(function () {
|
||||
$scheduledescription.val('');
|
||||
$schedulehour.val('0');
|
||||
$scheduleminute.val('0');
|
||||
$schedulesoundpath.val('');
|
||||
$schedulerepeat.val('0');
|
||||
|
||||
$schedulesoundpath.empty();
|
||||
if (Array.isArray(window.messagebankdata) && window.messagebankdata.length > 0) {
|
||||
window.messagebankdata.forEach(item => {
|
||||
let str = item.description+" ["+item.aNN_ID+"]";
|
||||
let option = `<option value="${str}">${str}</option>`;
|
||||
if ($schedulesoundpath.find(`option[value="${str}"]`).length === 0) $schedulesoundpath.append(option); // check if $schedulesoundpath already has this option
|
||||
})
|
||||
}
|
||||
|
||||
$schedulerepeat.val('1');
|
||||
$scheduleenable.prop('checked', true);
|
||||
$scheduleeveryday.prop('checked', false);
|
||||
$schedulesunday.prop('checked', false);
|
||||
@@ -158,24 +136,28 @@ $(document).ready(function () {
|
||||
$findschedule.on('input', function () {
|
||||
let searchTerm = $findschedule.val().toLowerCase();
|
||||
if (searchTerm.length > 0) {
|
||||
selectedtimerow = null;
|
||||
let filtered = schedulebankdata.filter(item =>
|
||||
window.selectedschedulerow = null;
|
||||
let filtered = window.schedulebankdata.filter(item =>
|
||||
item.description.toLowerCase().includes(searchTerm)
|
||||
|| item.soundpath.toLowerCase().includes(searchTerm)
|
||||
|| item.broadcastZones.toLowerCase().includes(searchTerm));
|
||||
fill_schedulebanktablebody(filtered);
|
||||
} else {
|
||||
selectedtimerow = null;
|
||||
fill_schedulebanktablebody(schedulebankdata);
|
||||
window.selectedschedulerow = null;
|
||||
fill_schedulebanktablebody(window.schedulebankdata);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
reloadTimerBank(APIURL);
|
||||
reloadTimerBank(APIURL, () => {
|
||||
fill_schedulebanktablebody(window.schedulebankdata);
|
||||
});
|
||||
$btnClear.click(() => {
|
||||
DoClear(APIURL, "Timerbank", (okdata) => {
|
||||
reloadTimerBank(APIURL);
|
||||
alert("Success clear schedulebank : " + okdata.message);
|
||||
reloadTimerBank(APIURL,() => {
|
||||
fill_schedulebanktablebody(window.schedulebankdata);
|
||||
alert("Success clear schedulebank : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error clear schedulebank : " + errdata.message);
|
||||
});
|
||||
@@ -236,8 +218,10 @@ $(document).ready(function () {
|
||||
};
|
||||
|
||||
fetchAPI(APIURL + "Add", "POST", {}, scheduleObj, (okdata) => {
|
||||
alert("Success add schedule: " + okdata.message);
|
||||
reloadTimerBank(APIURL);
|
||||
reloadTimerBank(APIURL, () => {
|
||||
fill_schedulebanktablebody(window.schedulebankdata);
|
||||
alert("Success add schedule: " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error add schedule: " + errdata.message);
|
||||
});
|
||||
@@ -246,8 +230,8 @@ $(document).ready(function () {
|
||||
});
|
||||
});
|
||||
$btnRemove.click(() => {
|
||||
if (selectedtimerow) {
|
||||
let cells = selectedtimerow.find('td');
|
||||
if (window.selectedschedulerow) {
|
||||
let cells = window.selectedschedulerow.find('td');
|
||||
/** @type {ScheduleBank} */
|
||||
let sr = {
|
||||
index: cells.eq(0).text(),
|
||||
@@ -262,8 +246,10 @@ $(document).ready(function () {
|
||||
}
|
||||
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);
|
||||
reloadTimerBank(APIURL, () => {
|
||||
fill_schedulebanktablebody(window.schedulebankdata);
|
||||
alert("Success delete schedule : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error delete schedule : " + errdata.message);
|
||||
});
|
||||
@@ -271,8 +257,8 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
$btnEdit.click(() => {
|
||||
if (selectedtimerow) {
|
||||
let cells = selectedtimerow.find('td');
|
||||
if (window.selectedschedulerow) {
|
||||
let cells = window.selectedschedulerow.find('td');
|
||||
/** @type {ScheduleBank} */
|
||||
let sr = {
|
||||
index: cells.eq(0).text(),
|
||||
@@ -380,8 +366,10 @@ $(document).ready(function () {
|
||||
};
|
||||
|
||||
fetchAPI(APIURL + "UpdateByIndex/" + sr.index, "PATCH", {}, scheduleObj, (okdata) => {
|
||||
alert("Success edit schedule: " + okdata.message);
|
||||
reloadTimerBank(APIURL);
|
||||
reloadTimerBank(APIURL, () => {
|
||||
fill_schedulebanktablebody(window.schedulebankdata);
|
||||
alert("Success edit schedule: " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error edit schedule: " + errdata.message);
|
||||
});
|
||||
@@ -396,8 +384,10 @@ $(document).ready(function () {
|
||||
});
|
||||
$btnImport.click(() => {
|
||||
DoImport(APIURL, (okdata) => {
|
||||
reloadTimerBank(APIURL);
|
||||
alert("Success import schedulebank from XLSX : " + okdata.message);
|
||||
reloadTimerBank(APIURL, () => {
|
||||
fill_schedulebanktablebody(window.schedulebankdata);
|
||||
alert("Success import schedulebank from XLSX : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error importing schedulebank from XLSX : " + errdata.message);
|
||||
});
|
||||
|
||||
@@ -2,23 +2,280 @@
|
||||
* List of voice types available
|
||||
* @type {string[]}
|
||||
*/
|
||||
let voiceTypes = [];
|
||||
window.voiceTypes = [];
|
||||
/**
|
||||
* List of categories available
|
||||
* @type {string[]}
|
||||
*/
|
||||
let categories = [];
|
||||
window.categories = [];
|
||||
/**
|
||||
* List of languages available
|
||||
* @type {string[]}
|
||||
*/
|
||||
let languages = [];
|
||||
window.languages = [];
|
||||
|
||||
/**
|
||||
* List of scheduled days available
|
||||
* @type {string[]}
|
||||
*/
|
||||
let scheduledays = []
|
||||
window.scheduledays = []
|
||||
|
||||
/**
|
||||
* @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
|
||||
* taruh di sini karena dipakai di banyak tempat
|
||||
* @type {MessageBank[]}
|
||||
*/
|
||||
window.messagebankdata ??= [];
|
||||
|
||||
/**
|
||||
* Reload message bank from server
|
||||
* @param {string} APIURL API URL endpoint, default "MessageBank/"
|
||||
* @param {Function|null} callback Optional callback function to execute after loading
|
||||
*/
|
||||
function reloadMessageBank(APIURL = "MessageBank/", callback=null) {
|
||||
window.messagebankdata ??= [];
|
||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||
if (Array.isArray(okdata)) {
|
||||
window.messagebankdata.push(...okdata);
|
||||
console.log("Loaded " + window.messagebankdata.length + " message bank items");
|
||||
window.selectedmessagerow = null;
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
}
|
||||
}, (errdata) => {
|
||||
alert("Error loading messagebank : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} ScheduleBank
|
||||
* @property {number} index
|
||||
* @property {string} description
|
||||
* @property {string} day
|
||||
* @property {string} time
|
||||
* @property {string} soundpath
|
||||
* @property {number} repeat
|
||||
* @property {boolean} enable
|
||||
* @property {string} broadcastZones
|
||||
* @property {string} language
|
||||
*/
|
||||
|
||||
/** List of Schedulebank data loaded from server
|
||||
* @type {ScheduleBank[]}
|
||||
*/
|
||||
window.schedulebankdata = [];
|
||||
|
||||
/**
|
||||
* Reload timer bank from server
|
||||
* taruh di sini karena dipakai di banyak tempat
|
||||
* @param {string} APIURL API URL endpoint, default "ScheduleBank/"
|
||||
* @param {Function|null} callback Optional callback function to execute after loading
|
||||
*/
|
||||
function reloadTimerBank(APIURL = "ScheduleBank/", callback=null) {
|
||||
window.schedulebankdata = [];
|
||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||
if (Array.isArray(okdata)) {
|
||||
window.schedulebankdata.push(...okdata);
|
||||
selectedschedulerow = null;
|
||||
console.log("Loaded " + window.schedulebankdata.length + " schedule bank items");
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
}
|
||||
}, (errdata) => {
|
||||
alert("Error loading schedulebank : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @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[]}
|
||||
*/
|
||||
window.BroadcastZoneList ??= [];
|
||||
|
||||
/**
|
||||
* Reload broadcast zones from server
|
||||
* taruh di sini karena dipakai di banyak tempat
|
||||
* @param {String} APIURL API URL endpoint (default "BroadcastZones/")
|
||||
* @param {Function|null} callback Optional callback function to execute after loading
|
||||
*/
|
||||
function reloadBroadcastZones(APIURL = "BroadcastZones/", callback=null) {
|
||||
window.BroadcastZoneList = [];
|
||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||
if (Array.isArray(okdata)) {
|
||||
//console.log("reloadBroadcastZones : ", okdata)
|
||||
window.BroadcastZoneList.push(...okdata);
|
||||
console.log("Loaded " + window.BroadcastZoneList.length + " broadcast zone items");
|
||||
window.selectedbroadcastzonerow = null;
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
} else console.log("reloadBroadcastZones: okdata is not array");
|
||||
}, (errdata) => {
|
||||
alert("Error loading broadcast zones : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
* taruh di sini karena dipakai di banyak tempat
|
||||
* @type {SoundBank[]}
|
||||
*/
|
||||
window.soundbankdata = [];
|
||||
|
||||
/**
|
||||
* List of sound files in the soundbank directory, that ends with .wav or .mp3
|
||||
* taruh di sini karena dipakai di banyak tempat
|
||||
* @type {string[]}
|
||||
*/
|
||||
window.soundbankfiles = [];
|
||||
|
||||
/**
|
||||
* Select2 data source
|
||||
* See https://select2.org/data-sources/formats
|
||||
* @type {Select2item[]}
|
||||
*/
|
||||
window.select2data = [];
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reload sound bank from server
|
||||
* @param {String} APIURL API URL endpoint, default "SoundBank/"
|
||||
* @param {Function|null} callback Optional callback function to execute after loading
|
||||
*/
|
||||
function reloadSoundBank(APIURL = "SoundBank/", callback=null) {
|
||||
window.soundbankdata = [];
|
||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||
|
||||
if (Array.isArray(okdata)) {
|
||||
window.soundbankdata.push(...okdata);
|
||||
window.selectedsoundrow = null;
|
||||
console.log("Loaded " + window.soundbankdata.length + " sound bank items");
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
}
|
||||
}, (errdata) => {
|
||||
alert("Error loading soundbank : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload soundbank files from server
|
||||
* @param {String} APIURL API URL endpoint (default "SoundBank/")
|
||||
* @param {Function|null} callback Optional callback function to execute after loading
|
||||
*/
|
||||
function reloadSoundbankFiles(APIURL = "SoundBank/",callback=null) {
|
||||
window.soundbankfiles = [];
|
||||
fetchAPI(APIURL + "ListFiles", "GET", {}, null, (okdata) => {
|
||||
// okdata is a string contains elements separated by semicolon ;
|
||||
if (Array.isArray(okdata)) {
|
||||
window.soundbankfiles = okdata.filter(item => item.trim().length > 0);
|
||||
// refill select2data
|
||||
window.select2data = window.soundbankfiles.map((item, index) => ({ id: index + 1, text: item }));
|
||||
console.log("Loaded " + window.soundbankfiles.length + " sound bank files");
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
} else console.log("reloadSoundbankFiles: okdata is not array");
|
||||
}, (errdata) => {
|
||||
alert("Error loading soundbank files : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @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[]}
|
||||
*/
|
||||
window.soundChannels = [];
|
||||
|
||||
/**
|
||||
* Reload sound channels from server
|
||||
* @param {String} APIURL API URL endpoint (default "SoundChannel/")
|
||||
* @param {Function|null} callback Optional callback function to execute after loading
|
||||
*/
|
||||
function reloadSoundChannel(APIURL = "SoundChannel/", callback=null) {
|
||||
window.soundChannels = [];
|
||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||
if (Array.isArray(okdata)) {
|
||||
//console.log("reloadSoundChannel : ", okdata)
|
||||
window.soundChannels.push(...okdata);
|
||||
window.selectedsoundchannelrow = null;
|
||||
console.log("Loaded " + window.soundChannels.length + " sound channel items");
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
} else console.log("reloadSoundChannel: okdata is not array");
|
||||
}, (errdata) => {
|
||||
alert("Error loading sound channels : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} LanguageBank
|
||||
* @property {number} index
|
||||
* @property {string} tag
|
||||
* @property {string} language
|
||||
*
|
||||
*/
|
||||
|
||||
/** List of Languagebank data loaded from server
|
||||
* @type {LanguageBank[]}
|
||||
*/
|
||||
window.languagebankdata = [];
|
||||
|
||||
/**
|
||||
* Reload language bank from server
|
||||
* @param {string} APIURL API URL endpoint, default "LanguageLink/"
|
||||
* @param {Function|null} callback Optional callback function to execute after loading
|
||||
*/
|
||||
function reloadLanguageBank(APIURL = "LanguageLink/", callback=null) {
|
||||
window.languagebankdata = [];
|
||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||
if (Array.isArray(okdata)) {
|
||||
window.languagebankdata.push(...okdata);
|
||||
window.selectedlanguagerow = null;
|
||||
console.log("Loaded " + window.languagebankdata.length + " language bank items");
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
}
|
||||
}, (errdata) => {
|
||||
alert("Error loading languagebank : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a list item element
|
||||
@@ -34,7 +291,7 @@ function ListItem(text, className = "") {
|
||||
* WebSocket connection
|
||||
* @type {WebSocket}
|
||||
*/
|
||||
let ws = null;
|
||||
window.ws = null;
|
||||
|
||||
/**
|
||||
* Send a command to the WebSocket server.
|
||||
@@ -42,8 +299,8 @@ let ws = null;
|
||||
* @param {String} data data to send
|
||||
*/
|
||||
function sendCommand(command, data) {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ command, data }));
|
||||
if (window.ws.readyState === WebSocket.OPEN) {
|
||||
window.ws.send(JSON.stringify({ command, data }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,11 +375,11 @@ function fetchImg(url, cbOK, cbError) {
|
||||
* Reload voice types from server
|
||||
*/
|
||||
function getVoiceTypes() {
|
||||
voiceTypes = [];
|
||||
window.voiceTypes = [];
|
||||
fetchAPI("VoiceType", "GET", {}, null, (okdata) => {
|
||||
// okdata is a string contains elements separated by semicolon ;
|
||||
if (Array.isArray(okdata)) {
|
||||
voiceTypes = okdata.filter(item => item.trim().length > 0);
|
||||
window.voiceTypes = okdata.filter(item => item.trim().length > 0);
|
||||
//console.log("Loaded " + voiceTypes.length + " voice types : " + voiceTypes.join(", "));
|
||||
} else console.log("getVoiceTypes: okdata is not array");
|
||||
}, (errdata) => {
|
||||
@@ -134,11 +391,11 @@ function getVoiceTypes() {
|
||||
* Reload categories from server
|
||||
*/
|
||||
function getCategories() {
|
||||
categories = [];
|
||||
window.categories = [];
|
||||
fetchAPI("Category", "GET", {}, null, (okdata) => {
|
||||
// okdata is a string contains elements separated by semicolon ;
|
||||
if (Array.isArray(okdata)) {
|
||||
categories = okdata.filter(item => item.trim().length > 0);
|
||||
window.categories = okdata.filter(item => item.trim().length > 0);
|
||||
//console.log("Loaded " + categories.length + " categories : " + categories.join(", "));
|
||||
} else console.log("getCategories: okdata is not array");
|
||||
}, (errdata) => {
|
||||
@@ -150,11 +407,11 @@ function getCategories() {
|
||||
* Reload languages from server
|
||||
*/
|
||||
function getLanguages() {
|
||||
languages = [];
|
||||
window.languages = [];
|
||||
fetchAPI("Language", "GET", {}, null, (okdata) => {
|
||||
// okdata is a string contains elements separated by semicolon ;
|
||||
if (Array.isArray(okdata)) {
|
||||
languages = okdata.filter(item => item.trim().length > 0);
|
||||
window.languages = okdata.filter(item => item.trim().length > 0);
|
||||
//console.log("Loaded " + languages.length + " languages : " + languages.join(", ") );
|
||||
} else console.log("getLanguages: okdata is not array");
|
||||
}, (errdata) => {
|
||||
@@ -166,11 +423,11 @@ function getLanguages() {
|
||||
* Reload scheduled days from server
|
||||
*/
|
||||
function getScheduledDays() {
|
||||
scheduledays = [];
|
||||
window.scheduledays = [];
|
||||
fetchAPI("ScheduleDay", "GET", {}, null, (okdata) => {
|
||||
// okdata is a string contains elements separated by semicolon ;
|
||||
if (Array.isArray(okdata)) {
|
||||
scheduledays = okdata.filter(item => item.trim().length > 0);
|
||||
window.scheduledays = okdata.filter(item => item.trim().length > 0);
|
||||
//console.log("Loaded " + scheduledays.length + " scheduled days : " + scheduledays.join(", ") );
|
||||
} else console.log("getScheduledDays: okdata is not array");
|
||||
}, (errdata) => {
|
||||
@@ -273,43 +530,36 @@ function DoImport(APIURL, cbOK, cbError) {
|
||||
fileInput.remove();
|
||||
}
|
||||
|
||||
let $onlineindicator = null;
|
||||
let $cpustatus = null;
|
||||
let $ramstatus = null;
|
||||
let $diskstatus = null;
|
||||
let $networkstatus = null;
|
||||
let $datetimetext = null;
|
||||
|
||||
let greencircle = null;
|
||||
let redcircle = null;
|
||||
window.greencircle = null;
|
||||
window.redcircle = null;
|
||||
/**
|
||||
* App entry point
|
||||
*/
|
||||
$(document).ready(function () {
|
||||
document.title = "Automatic Announcement System"
|
||||
fetchImg('green_circle.png', (url) => { greencircle = url; }, (err) => { console.error("Error loading green_circle.png : ", err); });
|
||||
fetchImg('red_circle.png', (url) => { redcircle = url; }, (err) => { console.error("Error loading red_circle.png : ", err); });
|
||||
if (window.greencircle === null){
|
||||
fetchImg('green_circle.png', (url) => { window.greencircle = url; }, (err) => { console.error("Error loading green_circle.png : ", err); });
|
||||
}
|
||||
if (window.redcircle === null){
|
||||
fetchImg('red_circle.png', (url) => { window.redcircle = url; }, (err) => { console.error("Error loading red_circle.png : ", err); });
|
||||
}
|
||||
const wsURL = window.location.pathname + '/ws'
|
||||
if (chrome && chrome.runtime && chrome.runtime.lastError) {
|
||||
alert("Runtime error: " + chrome.runtime.lastError.message);
|
||||
return;
|
||||
}
|
||||
$onlineindicator = $('#onlineindicator');
|
||||
$cpustatus = $('#cpustatus');
|
||||
$ramstatus = $('#ramstatus');
|
||||
$diskstatus = $('#diskstatus');
|
||||
$networkstatus = $('#networkstatus');
|
||||
$datetimetext = $('#datetimetext');
|
||||
|
||||
|
||||
|
||||
// reset status indicators
|
||||
function resetStatusIndicators() {
|
||||
$onlineindicator.attr('src', redcircle);
|
||||
$cpustatus.text("CPU : N/A");
|
||||
$ramstatus.text("RAM : N/A");
|
||||
$diskstatus.text("Disk : N/A");
|
||||
$networkstatus.text("Network : N/A");
|
||||
$datetimetext.text("Date/Time : N/A");
|
||||
$('#onlineindicator').attr('src', window.redcircle);
|
||||
$('#cpustatus').text("CPU : N/A");
|
||||
$('#ramstatus').text("RAM : N/A");
|
||||
$('#diskstatus').text("Disk : N/A");
|
||||
$('#networkstatus').text("Network : N/A");
|
||||
$('#datetimetext').text("Date/Time : N/A");
|
||||
}
|
||||
|
||||
|
||||
@@ -318,47 +568,56 @@ $(document).ready(function () {
|
||||
getCategories();
|
||||
getLanguages();
|
||||
getScheduledDays();
|
||||
|
||||
reloadMessageBank();
|
||||
reloadTimerBank();
|
||||
reloadBroadcastZones();
|
||||
reloadSoundBank();
|
||||
reloadSoundbankFiles();
|
||||
reloadSoundChannel();
|
||||
reloadLanguageBank();
|
||||
|
||||
// Initialize WebSocket connection
|
||||
ws = new WebSocket(wsURL);
|
||||
window.ws = new WebSocket(wsURL);
|
||||
|
||||
ws.onopen = () => {
|
||||
window.ws.onopen = () => {
|
||||
console.log('WebSocket connection established');
|
||||
$onlineindicator.attr('src', greencircle);
|
||||
$('#onlineindicator').attr('src', window.greencircle);
|
||||
};
|
||||
ws.onmessage = (event) => {
|
||||
window.ws.onmessage = (event) => {
|
||||
if ($('#onlineindicator').attr('src') !== window.greencircle) {
|
||||
$('#onlineindicator').attr('src', window.greencircle);
|
||||
}
|
||||
let rep = JSON.parse(event.data);
|
||||
let cmd = rep.reply
|
||||
let data = rep.data;
|
||||
if (cmd && cmd.length > 0) {
|
||||
switch (cmd) {
|
||||
case "getCPUStatus":
|
||||
$cpustatus.text("CPU : " + data)
|
||||
$('#cpustatus').text("CPU : " + data)
|
||||
break;
|
||||
case "getMemoryStatus":
|
||||
$ramstatus.text("RAM : " + data)
|
||||
$('#ramstatus').text("RAM : " + data)
|
||||
break;
|
||||
case "getDiskStatus":
|
||||
$diskstatus.text("Disk : " + data)
|
||||
$('#diskstatus').text("Disk : " + data)
|
||||
break;
|
||||
case "getNetworkStatus":
|
||||
$networkstatus.text("Network : " + data)
|
||||
$('#networkstatus').text("Network : " + data)
|
||||
break;
|
||||
case "getSystemTime":
|
||||
$datetimetext.text(data)
|
||||
$('#datetimetext').text(data)
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
ws.onclose = () => {
|
||||
window.ws.onclose = () => {
|
||||
console.log('WebSocket connection closed');
|
||||
resetStatusIndicators();
|
||||
|
||||
};
|
||||
|
||||
// ws.onerror = (error) => {
|
||||
// window.ws.onerror = (error) => {
|
||||
// console.error('WebSocket error:', error);
|
||||
// };
|
||||
|
||||
@@ -457,6 +716,18 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
})
|
||||
$('#usermanagement').click(() => {
|
||||
sidemenu.hide();
|
||||
$('#content').load('usermanagement.html', function (response, status, xhr) {
|
||||
if (status === "success") {
|
||||
console.log("User Management content loaded successfully");
|
||||
// pindah ke usermanagement.js
|
||||
|
||||
} else {
|
||||
console.error("Error loading user management content:", xhr.status, xhr.statusText);
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#settinglink').click(() => {
|
||||
sidemenu.hide();
|
||||
$('#content').load('setting.html', function (response, status, xhr) {
|
||||
|
||||
@@ -1,61 +1,11 @@
|
||||
/**
|
||||
* @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;
|
||||
window.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
|
||||
@@ -63,6 +13,7 @@ function reloadSoundBank(APIURL = "SoundBank/") {
|
||||
*/
|
||||
function fill_soundbanktablebody(vv) {
|
||||
$('#soundbanktablebody').empty();
|
||||
console.log("Filling soundbank table with " + vv.length + " items");
|
||||
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||
vv.forEach(item => {
|
||||
const row = `<tr>
|
||||
@@ -77,17 +28,17 @@ function fill_soundbanktablebody(vv) {
|
||||
$('#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;
|
||||
if (window.selectedsoundrow) {
|
||||
window.selectedsoundrow.find('td').css('background-color', '');
|
||||
if (window.selectedsoundrow.is($(this))) {
|
||||
window.selectedsoundrow = null;
|
||||
$('#btnRemove').prop('disabled', true);
|
||||
$('#btnEdit').prop('disabled', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$(this).find('td').css('background-color', '#ffeeba');
|
||||
selectedsoundrow = $(this);
|
||||
window.selectedsoundrow = $(this);
|
||||
$('#btnRemove').prop('disabled', false);
|
||||
$('#btnEdit').prop('disabled', false);
|
||||
});
|
||||
@@ -98,29 +49,13 @@ function fill_soundbanktablebody(vv) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
window.selectedsoundrow = null;
|
||||
let $btnClear = $('#btnClear');
|
||||
let $btnAdd = $('#btnAdd');
|
||||
let $btnRemove = $('#btnRemove');
|
||||
@@ -166,28 +101,33 @@ $(document).ready(function () {
|
||||
$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");
|
||||
console.log("window.select2data has " + window.select2data.length + " items");
|
||||
$('#modalpath').select2({
|
||||
data: select2data
|
||||
data: window.select2data
|
||||
})
|
||||
}
|
||||
|
||||
reloadSoundBank(APIURL);
|
||||
reloadSoundBank(APIURL, () => {
|
||||
fill_soundbanktablebody(window.soundbankdata);
|
||||
});
|
||||
|
||||
$('#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));
|
||||
window.selectedsoundrow = null;
|
||||
let filtered = window.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);
|
||||
window.selectedsoundrow = null;
|
||||
fill_soundbanktablebody(window.soundbankdata);
|
||||
}
|
||||
});
|
||||
$btnClear.click(() => {
|
||||
DoClear(APIURL, "Soundbank", (okdata) => {
|
||||
reloadSoundBank(APIURL);
|
||||
alert("Success clear soundbank : " + okdata.message);
|
||||
reloadSoundBank(APIURL, () => {
|
||||
fill_soundbanktablebody(window.soundbankdata);
|
||||
alert("Success clear soundbank : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error clear soundbank : " + errdata.message);
|
||||
});
|
||||
@@ -208,8 +148,8 @@ $(document).ready(function () {
|
||||
});
|
||||
});
|
||||
$btnRemove.click(() => {
|
||||
if (selectedsoundrow) {
|
||||
let cells = selectedsoundrow.find('td');
|
||||
if (window.selectedsoundrow) {
|
||||
let cells = window.selectedsoundrow.find('td');
|
||||
/** @type {SoundBank} */
|
||||
let sb = {
|
||||
index: cells.eq(0).text(),
|
||||
@@ -222,8 +162,10 @@ $(document).ready(function () {
|
||||
}
|
||||
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);
|
||||
reloadSoundBank(APIURL, () => {
|
||||
fill_soundbanktablebody(window.soundbankdata);
|
||||
alert("Success delete soundbank : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error delete soundbank : " + errdata.message);
|
||||
});
|
||||
@@ -231,8 +173,8 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
$btnEdit.click(() => {
|
||||
if (selectedsoundrow) {
|
||||
let cells = selectedsoundrow.find('td');
|
||||
if (window.selectedsoundrow) {
|
||||
let cells = window.selectedsoundrow.find('td');
|
||||
/** @type {SoundBank} */
|
||||
let sb = {
|
||||
index: cells.eq(0).text(),
|
||||
@@ -265,8 +207,10 @@ $(document).ready(function () {
|
||||
});
|
||||
$btnImport.click(() => {
|
||||
DoImport(APIURL, (okdata) => {
|
||||
reloadSoundBank(APIURL);
|
||||
alert("Success import soundbank : " + okdata.message);
|
||||
reloadSoundBank(APIURL, () => {
|
||||
fill_soundbanktablebody(window.soundbankdata);
|
||||
alert("Success import soundbank : " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error importing soundbank from XLSX : " + errdata.message);
|
||||
});
|
||||
|
||||
@@ -1,27 +1,18 @@
|
||||
/**
|
||||
* @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;
|
||||
window.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');
|
||||
let $tbody = $('#soundchanneltablebody');
|
||||
let $btnEditSoundChannel = $('#btnEditSoundChannel');
|
||||
let $tablesizeSoundChannel = $('#tablesizeSoundChannel');
|
||||
$tbody.empty();
|
||||
|
||||
$tablesizeSoundChannel.text('Table Length : N/A');
|
||||
if (!Array.isArray(vv) || vv.length === 0) return;
|
||||
|
||||
@@ -50,22 +41,7 @@ function fill_soundchanneltablebody(vv) {
|
||||
$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");
|
||||
@@ -85,12 +61,14 @@ $(document).ready(function () {
|
||||
$findsoundchannel.on('input', function () {
|
||||
let searchTerm = $(this).val().toLowerCase();
|
||||
if (searchTerm.length==0){
|
||||
fill_soundchanneltablebody(SoundChannelList);
|
||||
window.selectedSoundChannel = null;
|
||||
fill_soundchanneltablebody(window.soundChannels);
|
||||
} else {
|
||||
let filteredChannels = SoundChannelList.filter(channel =>
|
||||
channel.index.toString().includes(searchTerm) ||
|
||||
channel.description.toLowerCase().includes(searchTerm) ||
|
||||
channel.ip.toLowerCase().includes(searchTerm)
|
||||
window.selectedSoundChannel = null;
|
||||
let filteredChannels = window.soundChannels.filter(xx =>
|
||||
xx.index.toString().includes(searchTerm) ||
|
||||
xx.channel.toLowerCase().includes(searchTerm) ||
|
||||
xx.ip.toLowerCase().includes(searchTerm)
|
||||
);
|
||||
fill_soundchanneltablebody(filteredChannels);
|
||||
}
|
||||
@@ -105,11 +83,15 @@ $(document).ready(function () {
|
||||
$soundchannelip.val('');
|
||||
}
|
||||
|
||||
reloadSoundChannel(API_SoundChannel);
|
||||
reloadSoundChannel(API_SoundChannel, () => {
|
||||
fill_soundchanneltablebody(window.soundChannels);
|
||||
});
|
||||
$btnReinitializeSoundChannel.click(() => {
|
||||
DoClear(API_SoundChannel, "SoundChannels", (okdata) => {
|
||||
reloadSoundChannel(API_SoundChannel);
|
||||
alert("Success clear sound channels: " + okdata.message);
|
||||
reloadSoundChannel(API_SoundChannel, () => {
|
||||
fill_soundchanneltablebody(window.soundChannels);
|
||||
alert("Success clear sound channels: " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error clear sound channels: " + errdata.message);
|
||||
});
|
||||
@@ -153,8 +135,10 @@ $(document).ready(function () {
|
||||
|
||||
|
||||
fetchAPI(API_SoundChannel + "UpdateByIndex/" + newsc.index, "PATCH", {}, newsc, (okdata) => {
|
||||
reloadSoundChannel(API_SoundChannel);
|
||||
alert("Success edit sound channel: " + okdata.message);
|
||||
reloadSoundChannel(API_SoundChannel, () => {
|
||||
fill_soundchanneltablebody(window.soundChannels);
|
||||
alert("Success edit sound channel: " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error edit sound channel: " + errdata.message);
|
||||
});
|
||||
@@ -174,8 +158,10 @@ $(document).ready(function () {
|
||||
|
||||
$btnImportSoundChannel.click(() => {
|
||||
DoImport(API_SoundChannel, (okdata) => {
|
||||
reloadSoundChannel(API_SoundChannel);
|
||||
alert("Success import sound channels: " + okdata.message);
|
||||
reloadSoundChannel(API_SoundChannel, () => {
|
||||
fill_soundchanneltablebody(window.soundChannels);
|
||||
alert("Success import sound channels: " + okdata.message);
|
||||
});
|
||||
}, (errdata) => {
|
||||
alert("Error importing sound channels from XLSX: " + errdata.message);
|
||||
});
|
||||
|
||||
128
html/webpage/assets/js/usermanagement.js
Normal file
128
html/webpage/assets/js/usermanagement.js
Normal file
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* @typedef {Object} UserDB
|
||||
* @property {number} index Index number
|
||||
* @property {string} username Username
|
||||
* @property {string} password Password (plain)
|
||||
* @property {string} location Location
|
||||
* @property {string} soundbank_tags Soundbank variable tags separated by semicolon ;
|
||||
* @property {string} messagebank_ann_id Messagebank announcement ID separated by semicolon ;
|
||||
* @property {string} broadcastzones Broadcast zones separated by semicolon ;
|
||||
*/
|
||||
|
||||
/** List of UserDB data loaded from server
|
||||
* @type {UserDB[]}
|
||||
*/
|
||||
window.userdb = [];
|
||||
|
||||
/**
|
||||
* Currently selected user row in table
|
||||
* @type {JQuery<HTMLElement>|null}
|
||||
*/
|
||||
window.selecteduserrow = null;
|
||||
|
||||
/**
|
||||
* Fill user table body with values
|
||||
* @param {UserDB[]} vv values to fill
|
||||
*/
|
||||
function fill_usertablebody(vv) {
|
||||
$('#usertablebody').empty();
|
||||
|
||||
if (!Array.isArray(vv) || vv.length === 0) {
|
||||
$('#btnExport').prop('disabled', true);
|
||||
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>`;
|
||||
$('#usertablebody').append(row);
|
||||
let $addedrow = $('#usertablebody tr:last');
|
||||
$addedrow.on('click', function () {
|
||||
if (window.selecteduserrow) {
|
||||
window.selecteduserrow.find('td').css('background-color', '');
|
||||
if (window.selecteduserrow.is($(this))) {
|
||||
window.selecteduserrow = null;
|
||||
$('#btnRemove').prop('disabled', true);
|
||||
$('#btnEdit').prop('disabled', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$(this).find('td').css('background-color', '#ffeeba');
|
||||
window.selecteduserrow = $(this);
|
||||
$('#btnRemove').prop('disabled', false);
|
||||
$('#btnEdit').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
$('#tablesize').text("Table Size: " + vv.length);
|
||||
$('#btnExport').prop('disabled', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload UserDB from server with date and filter
|
||||
* @param {String} APIURL API URL endpoint , default "User/"
|
||||
*/
|
||||
function reloaduserDB(APIURL = "User/") {
|
||||
window.userdb = [];
|
||||
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
|
||||
if (Array.isArray(okdata)) {
|
||||
window.userdb.push(...okdata);
|
||||
fill_usertablebody(window.userdb);
|
||||
}
|
||||
}, (errdata) => {
|
||||
alert("Error loading user database : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log("usermanagement.js ready");
|
||||
let $usertablebody = $('#usertablebody');
|
||||
let $finduser = $('#finduser');
|
||||
let $btnClear = $('#btnClear');
|
||||
let $btnAdd = $('#btnAdd');
|
||||
let $btnRemove = $('#btnRemove');
|
||||
let $btnEdit = $('#btnEdit');
|
||||
let $btnExport = $('#btnExport');
|
||||
let $btnImport = $('#btnImport');
|
||||
let APIURL = "User/";
|
||||
|
||||
// add / edit modal elements
|
||||
let $addmodal = $('#addmodal');
|
||||
let $modalindex = $('#modalindex');
|
||||
let $modalusername = $('#modalusername');
|
||||
let $modalpassword = $('#modalpassword');
|
||||
let $modalverifypassword = $('#modalverifypassword');
|
||||
let $modalsoundbank = $('#modalsoundbank');
|
||||
let $modalmessagebank = $('#modalmessagebank');
|
||||
let $modalbroadcastzones = $('#modalbroadcastzones');
|
||||
let $btnShowSoundbankModal = $('#btnShowSoundbankModal');
|
||||
let $btnShowMessagebankModal = $('#btnShowMessagebankModal');
|
||||
let $btnShowBroaadcastZoneModal = $('#btnShowBroaadcastZoneModal');
|
||||
let $usermanagementsave = $('#usermanagementsave');
|
||||
let $usermanagementclose = $('#usermanagementclose');
|
||||
|
||||
// soundbank selection modal elements
|
||||
let $soundbankmodal = $('#soundbankmodal');
|
||||
let $soundbankselection = $('#soundbankselection');
|
||||
let $soundbankselectionsave = $('#soundbankselectionsave');
|
||||
let $soundbankselectionclose = $('#soundbankselectionclose');
|
||||
|
||||
// broadcast zone selection modal elements
|
||||
let $broadcastzonemodal = $('#broadcastzonemodal');
|
||||
let $broadcastzoneselection = $('#broadcastzoneselection');
|
||||
let $broadcastzoneselectionsave = $('#broadcastzoneselectionsave');
|
||||
let $broadcastzoneselectionclose = $('#broadcastzoneselectionclose');
|
||||
|
||||
// messagebank selection modal elements
|
||||
let $messagebankmodal = $('#messagebankmodal');
|
||||
let $messagebankselection = $('#messagebankselection');
|
||||
let $messagebankselectionsave = $('#messagebankselectionsave');
|
||||
let $messagebankselectionclose = $('#messagebankselectionclose');
|
||||
|
||||
$usertablebody.empty();
|
||||
$finduser.on('input', function () {
|
||||
});
|
||||
});
|
||||
@@ -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_NewGen_30Sept25</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">
|
||||
@@ -24,34 +24,34 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<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 class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><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">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Description</p>
|
||||
</div>
|
||||
<div class="col"><input type="text" id="broadcastzonedescription" class="form-control input-add" placeholder="description"></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="broadcastzonedescription" class="form-control input-add" placeholder="description"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<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 class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="broadcastzonesoundchannel" class="form-control input-add" placeholder="sound channel"></select></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<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 class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><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">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Relay</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8 pad-relay">
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<div class="row">
|
||||
@@ -167,9 +167,9 @@
|
||||
</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-item pad-accordion">
|
||||
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed bg-heading1" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-1" aria-expanded="false" aria-controls="accordion-1 .item-1">Sound Channel</button></h2>
|
||||
<div class="accordion-collapse collapse 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>
|
||||
@@ -178,11 +178,11 @@
|
||||
</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 class="row pad-search">
|
||||
<div class="col-6 col-sm-6 col-md-3 col-lg-3 col-xl-3"><button class="btn w-100 pad-button btn-round-basic color-add" id="btnReinitializeSoundChannel" type="button">Re-Initialize</button></div>
|
||||
<div class="col-6 col-sm-6 col-md-3 col-lg-3 col-xl-3"><button class="btn w-100 pad-button btn-round-basic color-edit" id="btnEditSoundChannel" type="button">Edit</button></div>
|
||||
<div class="col-6 col-md-3 col-lg-3 col-xl-3 col-sm--6"><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-3 col-lg-3 col-xl-3"><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">
|
||||
@@ -206,9 +206,9 @@
|
||||
</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-item pad-accordion">
|
||||
<h2 class="accordion-header" role="tab"><button class="accordion-button bg-heading2" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-2" aria-expanded="true" aria-controls="accordion-1 .item-2">Broadcast Zones</button></h2>
|
||||
<div class="accordion-collapse collapse show 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>
|
||||
@@ -217,7 +217,7 @@
|
||||
</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="row pad-search">
|
||||
<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>
|
||||
@@ -238,8 +238,8 @@
|
||||
<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>
|
||||
<th class="col-sm-2">ID</th>
|
||||
<th class="col">BP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="broadcastzonetablebody"></tbody>
|
||||
@@ -259,25 +259,25 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<p>Index</p>
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Index</p>
|
||||
</div>
|
||||
<div class="col"><input class="w-25" type="text" id="soundchannelindex" readonly="" placeholder="index"></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-xl-8 co-lg-8"><input class="w-25 form-control input-add" type="text" id="soundchannelindex" readonly="" placeholder="index"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<p>Description</p>
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Description</p>
|
||||
</div>
|
||||
<div class="col"><input class="w-100" type="text" id="soundchanneldescription" placeholder="Description"></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-xl-8 co-lg-8"><input class="w-100 form-control input-add" type="text" id="soundchanneldescription" placeholder="Description"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<p>IP Address</p>
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">IP Address</p>
|
||||
</div>
|
||||
<div class="col"><input class="w-100" type="text" id="soundchannelip" placeholder="IP Address"></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-xl-8 co-lg-8"><input class="w-100 form-control input-add" 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 class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="soundchannelclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="soundchannelsave" type="button">Save</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -60,6 +60,10 @@
|
||||
<path d="M19,7H9C7.9,7,7,7.9,7,9v10c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2V9C21,7.9,20.1,7,19,7z M19,9v2H9V9H19z M13,15v-2h2v2H13z M15,17v2h-2v-2H15z M11,15H9v-2h2V15z M17,13h2v2h-2V13z M9,17h2v2H9V17z M17,19v-2h2v2H17z M6,17H5c-1.1,0-2-0.9-2-2V5 c0-1.1,0.9-2,2-2h10c1.1,0,2,0.9,2,2v1h-2V5H5v10h1V17z"></path>
|
||||
</g>
|
||||
</svg> Log</a></li>
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="usermanagement" 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="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"></path>
|
||||
</svg> User Management</a></li>
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="settinglink" 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="me-2 icon-menu" style="font-size: 20px;">
|
||||
<g>
|
||||
<path d="M0,0h24v24H0V0z" fill="none"></path>
|
||||
|
||||
@@ -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_NewGen_30Sept25</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">
|
||||
@@ -16,7 +16,7 @@
|
||||
<h2 style="text-align: center;">Language Link</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row pad-row-search">
|
||||
<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>
|
||||
@@ -58,22 +58,22 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Index</p>
|
||||
</div>
|
||||
<div class="col"><input class="w-25 form-control input-add" type="text" id="languagelinkindex" readonly=""></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><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">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Tag</p>
|
||||
</div>
|
||||
<div class="col"><input type="text" id="languagelinktag" class="form-control input-add"></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="languagelinktag" class="form-control input-add"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Language</p>
|
||||
</div>
|
||||
<div class="col text-add">
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8 text-add">
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="langId" name="languages[]" value="id"><label class="form-check-label" for="langId">Indonesia</label></div>
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="langLocal" name="languages[]" value="id"><label class="form-check-label" for="langId-1">Local</label></div>
|
||||
<div class="form-check"><input class="form-check-input" type="checkbox" id="langEn" name="languages[]" value="en"><label class="form-check-label" for="langEn">English</label></div>
|
||||
|
||||
@@ -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_NewGen_30Sept25</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">
|
||||
@@ -17,15 +17,16 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 col-sm col-md-2 col-lg-2 col-xl-2">
|
||||
<div class="col-4 col-sm-4 col-md-2 col-lg-2 col-xl-2">
|
||||
<p class="text-add">Select Log Date</p>
|
||||
</div>
|
||||
<div class="col-6 col-sm col-md-2 col-lg-2 col-xl-2"><input id="logdate" class="form-control" type="date"></div>
|
||||
<div class="col-md-4 col-lg-4 col-xl-4"><button class="btn btn-primary w-100 h-100" id="btnExport" type="button">Export</button></div>
|
||||
<div class="col-6 col-sm col-md-2 col-lg-2 col-xl-2">
|
||||
<div class="col-4 col-sm-4 col-md-2 col-lg-2 col-xl-2"><input id="logdate" class="form-control" type="date"></div>
|
||||
<div class="col-4 col-sm-4 col-md-2 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-md-2 col-lg-2 col-xl-2"></div>
|
||||
<div class="col-4 col-sm-4 col-md-2 col-lg-2 col-xl-2">
|
||||
<p class="text-add">Search</p>
|
||||
</div>
|
||||
<div class="col-6 col-sm col-md-2 col-lg-2 col-xl-2"><input type="text" id="searchfilter" class="form-control" placeholder="Search Filter"></div>
|
||||
<div class="col-4 col-sm-4 col-md-2 col-lg-2 col-xl-2"><input type="text" id="searchfilter" class="form-control" placeholder="Search Filter"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
|
||||
@@ -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_NewGen_30Sept25</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">
|
||||
@@ -16,12 +16,12 @@
|
||||
<h2 style="text-align: center;">Message Bank</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row pad-row-search">
|
||||
<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">
|
||||
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2">
|
||||
<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="findmessage" placeholder="Search keyword" name="findsoundbank"></div>
|
||||
<div class="col-10 col-sm-10 col-md-3 col-lg-3 col-xl-3"><input class="w-100 form-control pad-search" type="text" id="findmessage" 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>
|
||||
@@ -62,22 +62,22 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Index</p>
|
||||
</div>
|
||||
<div class="col"><input class="w-25 input-add form-control" type="text" id="messageindex" placeholder="Index" readonly=""></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><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">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Description</p>
|
||||
</div>
|
||||
<div class="col"><input type="text" id="messagedescription" class="input-add form-control" placeholder="Description"></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="messagedescription" class="input-add form-control" placeholder="Description"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Language</p>
|
||||
</div>
|
||||
<div class="col"><select id="messagelanguage" class="input-add form-control">
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="messagelanguage" class="input-add form-control">
|
||||
<option value="INDONESIA">Indonesia</option>
|
||||
<option value="LOCAL">Local</option>
|
||||
<option value="ENGLISH">English</option>
|
||||
@@ -87,16 +87,16 @@
|
||||
</select></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">ANN ID</p>
|
||||
</div>
|
||||
<div class="col"><input type="number" id="messageannid" class="input-add form-control" min="1" max="100" value="1" step="1" placeholder="Announcement ID"></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="number" id="messageannid" class="input-add form-control" min="1" max="100" value="1" step="1" placeholder="Announcement ID"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Voice Type</p>
|
||||
</div>
|
||||
<div class="col"><select id="messagevoicetype" class="input-add form-control">
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="messagevoicetype" class="input-add form-control">
|
||||
<option value="VOICE_1">Voice 1</option>
|
||||
<option value="VOICE_2">Voice 2</option>
|
||||
<option value="VOICE_3">Voice 3</option>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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_NewGen_30Sept25</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">
|
||||
|
||||
@@ -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_NewGen_30Sept25</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">
|
||||
@@ -62,46 +62,46 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Index</p>
|
||||
</div>
|
||||
<div class="col"><input class="w-25 input-add form-control" type="text" id="modalindex" readonly=""></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><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">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Description</p>
|
||||
</div>
|
||||
<div class="col"><input type="text" id="modaldescription" class="form-control input-add"></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="modaldescription" class="form-control input-add"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">TAG</p>
|
||||
</div>
|
||||
<div class="col"><input type="text" id="modaltag" class="form-control input-add"></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="modaltag" class="form-control input-add"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Category</p>
|
||||
</div>
|
||||
<div class="col"><select id="modalcategory" class="input-add form-select"></select></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="modalcategory" class="input-add form-select"></select></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Language</p>
|
||||
</div>
|
||||
<div class="col"><select id="modallanguage" class="input-add form-select"></select></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="modallanguage" class="input-add form-select"></select></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Voice Type</p>
|
||||
</div>
|
||||
<div class="col"><select id="modalvoicetype" class="input-add form-select"></select></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="modalvoicetype" class="input-add form-select"></select></div>
|
||||
</div>
|
||||
<div class="row" style="height: 100px;">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Path</p>
|
||||
</div>
|
||||
<div class="col"><select class="w-100 js-example-basic-single" id="modalpath" name="modalpath"></select></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select class="w-100 js-example-basic-single input-add form-select" id="modalpath" name="modalpath"></select></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="soundbankclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="soundbanksave" type="button">Save</button></div>
|
||||
|
||||
@@ -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_NewGen_30Sept25</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">
|
||||
@@ -12,13 +12,13 @@
|
||||
|
||||
<body>
|
||||
<div class="card" id="streamercard">
|
||||
<div class="card-body">
|
||||
<div class="card-body card-channel">
|
||||
<h4 class="card-title" id="streamertitle">Channel 01</h4>
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<div class="col-12 col-sm-12 col-md-12 col-lg-6 col-xl-6 col-xxl-6">
|
||||
<h6 class="text-muted mb-2" id="streamerip">IP : 192.168.10.10</h6>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="col-12 col-sm-12 col-md-12 col-lg-6 col-xl-6 col-xxl-6">
|
||||
<h6 class="text-muted mb-2" id="streamerbuffer">Free : 64KB</h6>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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_NewGen_30Sept25</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">
|
||||
@@ -16,7 +16,7 @@
|
||||
<h2 style="text-align: center;">Schedule Bank</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row pad-row-search">
|
||||
<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>
|
||||
@@ -63,110 +63,110 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Index</p>
|
||||
</div>
|
||||
<div class="col"><input class="w-25 input-add form-control" type="text" id="scheduleid" readonly=""></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><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">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Description</p>
|
||||
</div>
|
||||
<div class="col"><input type="text" id="scheduledescription" class="input-add form-control"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Day</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="row pad-day">
|
||||
<div class="col">
|
||||
<div class="form-check"><input class="form-check-input" type="radio" id="scheduleeveryday" name="dayselection" value="Everyday"><label class="form-check-label" for="formCheck-1">Everyday</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row pad-day">
|
||||
<div class="col">
|
||||
<div class="form-check"><input class="form-check-input" type="radio" id="schedulesunday" name="dayselection" value="Sunday"><label class="form-check-label" for="formCheck-8">Sunday</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row pad-day">
|
||||
<div class="col">
|
||||
<div class="form-check"><input class="form-check-input" type="radio" id="schedulemonday" name="dayselection" value="Monday"><label class="form-check-label" for="formCheck-7">Monday</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row pad-day">
|
||||
<div class="col">
|
||||
<div class="form-check"><input class="form-check-input" type="radio" id="scheduletuesday" name="dayselection" value="Tuesday"><label class="form-check-label" for="formCheck-6">Tuesday</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row pad-day">
|
||||
<div class="col">
|
||||
<div class="form-check"><input class="form-check-input" type="radio" id="schedulewednesday" name="dayselection" value="Wednesday"><label class="form-check-label" for="formCheck-5">Wednesday</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row pad-day">
|
||||
<div class="col">
|
||||
<div class="form-check"><input class="form-check-input" type="radio" id="schedulethursday" name="dayselection" value="Thursday"><label class="form-check-label" for="formCheck-4">Thursday</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row pad-day">
|
||||
<div class="col">
|
||||
<div class="form-check"><input class="form-check-input" type="radio" id="schedulefriday" name="dayselection" value="Friday"><label class="form-check-label" for="formCheck-3">Friday</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row pad-day">
|
||||
<div class="col">
|
||||
<div class="form-check"><input class="form-check-input" type="radio" id="schedulesaturday" name="dayselection" value="Saturday"><label class="form-check-label" for="formCheck-2">Saturday</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="col-7 col-sm-7 col-md-7 col-lg-6 col-xl-6 pad-day">
|
||||
<div class="form-check"><input class="form-check-input" type="radio" id="schedulespecialdate" name="dayselection"><label class="form-check-label" for="formCheck-9">Special Date</label></div>
|
||||
</div>
|
||||
<div class="col"><input id="scheduledate" type="date"></div>
|
||||
<div class="col-sm-5 col-md-5 col-lg-6 col-xl-6"><input id="scheduledate" class="form-control" type="date"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Time</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row w-100 h-100">
|
||||
<div class="col-3"><input class="w-100 h-100" type="number" id="schedulehour" value="0" min="0" max="23" step="1"></div>
|
||||
<div class="col-1">
|
||||
<p class="w-100 h-100">(H)</p>
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3"><input type="number" id="schedulehour" class="input-add form-control class100" value="0" min="0" max="23" step="1"></div>
|
||||
<div class="col-2 col-sm-2 col-md-2 col-lg-3 col-xl-3">
|
||||
<p class="pad-time">(H)</p>
|
||||
</div>
|
||||
<div class="col-3"><input class="w-100 h-100" type="number" id="scheduleminute" value="0" min="0" max="59" step="1"></div>
|
||||
<div class="col-1">
|
||||
<p class="w-100 h-100">(M)</p>
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3"><input class="w-100 input-add form-control" type="number" id="scheduleminute" value="0" min="0" max="59" step="1"></div>
|
||||
<div class="col-2 col-sm-2 col-md-2 col-lg-3 col-xl-3">
|
||||
<p class="pad-time">(M)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Sound Path</p>
|
||||
</div>
|
||||
<div class="col"><input type="text" id="schedulesoundpath" class="input-add form-control"></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="schedulesoundpath" class="input-add form-control"></select></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Repeat</p>
|
||||
</div>
|
||||
<div class="col"><input class="w-25 form-select input-add" type="number" id="schedulerepeat" min="0" max="5" step="1" value="0"></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input class="w-25 form-select input-add" type="number" id="schedulerepeat" min="0" max="5" step="1" value="0"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Enable</p>
|
||||
</div>
|
||||
<div class="col"><input type="checkbox" id="scheduleenable" class="form-check-input form-check text-add" checked=""></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="checkbox" id="scheduleenable" class="form-check-input form-check text-add" checked=""></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-3">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Broadcast Zones</p>
|
||||
</div>
|
||||
<div class="col border p-2" id="schedulezones"></div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8 border" id="schedulezones"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="scheduleclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic class25 color-add" id="schedulesave" type="button">Save</button></div>
|
||||
|
||||
153
html/webpage/usermanagement.html
Normal file
153
html/webpage/usermanagement.html
Normal file
@@ -0,0 +1,153 @@
|
||||
<!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_30Sept25</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 class="bg-body">
|
||||
<div class="row">
|
||||
<div class="col w-100 h-100 pad-header">
|
||||
<h2 style="text-align: center;">User Management</h2>
|
||||
</div>
|
||||
</div>
|
||||
<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="finduser" placeholder="Search keyword" name="findsoundbank"></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="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="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-sm-1">No</th>
|
||||
<th class="col-sm-1">Username</th>
|
||||
<th class="col-sm-1">Location</th>
|
||||
<th class="col">Soundbank</th>
|
||||
<th class="col-sm-2">Messagebank</th>
|
||||
<th class="col-sm-2">Broadcast Zones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="usertablebody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade border-0" role="dialog" tabindex="-1" id="addmodal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header show">
|
||||
<h4 class="modal-title align-content-center">Add / Edit User</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Index</p>
|
||||
</div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input class="w-25 input-add form-control" type="text" id="modalindex" readonly=""></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Username</p>
|
||||
</div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="modalusername" class="form-control input-add"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Password</p>
|
||||
</div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="password" id="modalpassword" class="form-control input-add"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Verify Password</p>
|
||||
</div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="password" id="modalverifypassword" class="input-add form-control"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Sound Bank</p>
|
||||
</div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="modalsoundbank" class="input-add"><button class="btn btn-primary" id="btnShowSoundbankModal" type="button">Select</button></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Message Bank</p>
|
||||
</div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="modalmessagebank" class="input-add"><button class="btn btn-primary" id="btnShowMessagebankModal" type="button">Select</button></div>
|
||||
</div>
|
||||
<div class="row" style="height: 100px;">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
|
||||
<p class="text-add">Broadcast Zones</p>
|
||||
</div>
|
||||
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="modalbroadcastzones" class="input-add" name="modalpath"><button class="btn btn-primary" id="btnShowBroaadcastZoneModal" type="button">Select</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="usermanagementclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="usermanagementsave" type="button">Save</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" role="dialog" tabindex="-1" id="soundbankmodal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Sound Bank Selection</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>(mon, ganti list checkbox, dengan id=soundbankselection</p>
|
||||
</div>
|
||||
<div class="modal-footer"><button class="btn btn-light" id="soundbankselectionclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-primary" id="soundbankselectionsave" type="button">Save</button></div>
|
||||
</div>
|
||||
</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">Broadcast Zones Selection</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>(mon, ganti list checkbox, dengan id=broadcastzoneselection</p>
|
||||
</div>
|
||||
<div class="modal-footer"><button class="btn btn-light" id="broadcastzoneselectionclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-primary" id="broadcastzoneselectionsave" type="button">Save</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" role="dialog" tabindex="-1" id="messagebankmodal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Message Bank Selection</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>(mon, ganti list checkbox, dengan id=messagebankselection</p>
|
||||
</div>
|
||||
<div class="modal-footer"><button class="btn btn-light" id="messagebankselectionclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-primary" id="messagebankselectionsave" type="button">Save</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/usermanagement.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
18
src/Main.kt
18
src/Main.kt
@@ -1,4 +1,5 @@
|
||||
import audio.AudioPlayer
|
||||
import audio.UDPReceiver
|
||||
import barix.BarixConnection
|
||||
import barix.TCP_Barix_Command_Server
|
||||
import com.sun.jna.Platform
|
||||
@@ -20,7 +21,10 @@ import kotlin.concurrent.fixedRateTimer
|
||||
lateinit var db: MariaDB
|
||||
lateinit var audioPlayer: AudioPlayer
|
||||
val StreamerOutputs: MutableMap<String, BarixConnection> = HashMap()
|
||||
lateinit var udpreceiver: UDPReceiver
|
||||
const val version = "0.0.2 (23/09/2025)"
|
||||
// AAS 64 channels
|
||||
const val max_channel = 64
|
||||
|
||||
// dipakai untuk pilih voice type, bisa diganti via web nanti
|
||||
var selected_voice = VoiceType.VOICE_1.name
|
||||
@@ -74,16 +78,22 @@ fun main() {
|
||||
))
|
||||
web.Start()
|
||||
|
||||
udpreceiver = UDPReceiver()
|
||||
if (udpreceiver.Start()) {
|
||||
Logger.info { "UDP Receiver started on port 5002" }
|
||||
} else {
|
||||
Logger.error { "Failed to start UDP Receiver on port 5002" }
|
||||
}
|
||||
|
||||
val androidserver = TCP_Android_Command_Server()
|
||||
androidserver.StartTcpServer(5003){
|
||||
Logger.info { it }
|
||||
|
||||
db.logDB.Add(Log.NewLog("ANDROID", it))
|
||||
}
|
||||
|
||||
val barixserver = TCP_Barix_Command_Server()
|
||||
barixserver.StartTcpServer { cmd ->
|
||||
Logger.info { cmd }
|
||||
//Logger.info { cmd }
|
||||
val _streamer = StreamerOutputs[cmd.ipaddress]
|
||||
val _sc = db.soundchannelDB.List.find { it.ip == cmd.ipaddress }
|
||||
if (_streamer == null) {
|
||||
@@ -95,7 +105,8 @@ fun main() {
|
||||
_bc.bufferRemain = cmd.buffremain
|
||||
_bc.statusData = cmd.statusdata
|
||||
StreamerOutputs[cmd.ipaddress] = _bc
|
||||
}
|
||||
Logger.info { "Created new Streamer Output for channel ${_sc.channel} with IP ${cmd.ipaddress}" }
|
||||
} else Logger.warn { "soundChannelDB doesn't have soundchannel with IP ${cmd.ipaddress}" }
|
||||
|
||||
} else {
|
||||
// sudah ada, update data
|
||||
@@ -123,6 +134,7 @@ fun main() {
|
||||
androidserver.StopTcpCommand()
|
||||
onlinechecker.cancel()
|
||||
web.Stop()
|
||||
udpreceiver.Stop()
|
||||
audioPlayer.Close()
|
||||
db.close()
|
||||
Logger.info { "All services stopped, exiting application." }
|
||||
|
||||
@@ -7,15 +7,18 @@ import codes.Somecodes.Companion.SoundbankResult_directory
|
||||
import codes.Somecodes.Companion.ValidFile
|
||||
import codes.Somecodes.Companion.ValidString
|
||||
import codes.Somecodes.Companion.dateformat1
|
||||
import codes.Somecodes.Companion.datetimeformat1
|
||||
import codes.Somecodes.Companion.timeformat2
|
||||
import content.Category
|
||||
import content.Language
|
||||
import content.ScheduleDay
|
||||
import database.Messagebank
|
||||
import database.QueueTable
|
||||
import database.Soundbank
|
||||
import org.tinylog.Logger
|
||||
import java.time.DayOfWeek
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.LocalTime
|
||||
import java.util.function.Consumer
|
||||
|
||||
@@ -36,16 +39,24 @@ class MainExtension01 {
|
||||
*/
|
||||
fun AllBroadcastZonesValid(bz: List<String>): Boolean {
|
||||
if (bz.isNotEmpty()) {
|
||||
println("StreamerOutputs: $StreamerOutputs")
|
||||
val validchannels = bz
|
||||
// check apakah tiap zone ada di database broadcast zones
|
||||
.filter { z1 ->
|
||||
db.broadcastDB.List.find { z2 -> z2.SoundChannel == z1 } != null
|
||||
println("Checking broadcast zone $z1")
|
||||
val xx = db.broadcastDB.List.find { z2 -> z2.description == z1 }
|
||||
println("Found in DB: $xx")
|
||||
xx != null
|
||||
}
|
||||
// check apakah tiap zone ada di SoundChannelList dan Online
|
||||
.filter { z3 ->
|
||||
StreamerOutputs.any { sc -> sc.value.channel == z3 && sc.value.isOnline() }
|
||||
println("Checking if zone $z3 is in StreamerOutputs and online")
|
||||
val xx = StreamerOutputs.values.find { it.channel == z3 }
|
||||
println("Is online: $xx")
|
||||
xx!= null
|
||||
}
|
||||
|
||||
println("Valid channels: $validchannels")
|
||||
// kalau jumlah valid channel sama dengan jumlah broadcast zone, berarti semua valid
|
||||
return validchannels.size == bz.size
|
||||
}
|
||||
@@ -481,6 +492,7 @@ class MainExtension01 {
|
||||
fun Read_Queue_Paging(){
|
||||
db.queuepagingDB.Get()
|
||||
for (qp in db.queuepagingDB.List) {
|
||||
println("Processing QueuePaging $qp")
|
||||
if (qp.BroadcastZones.isNotBlank()) {
|
||||
val zz = qp.BroadcastZones.split(";")
|
||||
if (AllBroadcastZonesValid(zz)) {
|
||||
@@ -612,10 +624,14 @@ class MainExtension01 {
|
||||
fun Read_Queue_Table(){
|
||||
db.queuetableDB.Get()
|
||||
db.queuetableDB.List.forEach { qa ->
|
||||
println("Processing QueueTable $qa")
|
||||
if (qa.BroadcastZones.isNotEmpty()) {
|
||||
val zz = qa.BroadcastZones.split(";")
|
||||
println("Broadcast zones: $zz")
|
||||
if (AllBroadcastZonesValid(zz)) {
|
||||
println("All broadcast zones valid")
|
||||
if (AllBroadcastZoneIdle(zz)) {
|
||||
println("All broadcast zones idle")
|
||||
if (qa.Type == "SOUNDBANK") {
|
||||
val variables = Get_Soundbank_Data(qa.SB_TAGS)
|
||||
val languages = qa.Language.split(";")
|
||||
@@ -809,7 +825,19 @@ class MainExtension01 {
|
||||
it.Day == ddmmyyyy
|
||||
}
|
||||
if (specialdate != null) {
|
||||
// TODO Masukin ke queue table sebagai schedule special date
|
||||
val qt = QueueTable(
|
||||
0u,
|
||||
LocalDateTime.now().format(datetimeformat1),
|
||||
"AAS",
|
||||
"TIMER",
|
||||
specialdate.Description,
|
||||
specialdate.Soundpath,
|
||||
specialdate.BroadcastZones,
|
||||
1.toUInt(),
|
||||
specialdate.Language
|
||||
)
|
||||
db.queuetableDB.Add(qt)
|
||||
return
|
||||
|
||||
}
|
||||
// cek weekly schedule
|
||||
@@ -825,7 +853,19 @@ class MainExtension01 {
|
||||
}
|
||||
}
|
||||
if (weekly != null) {
|
||||
// TODO Masukin ke queue table sebagai schedule weekly
|
||||
val qt = QueueTable(
|
||||
0u,
|
||||
LocalDateTime.now().format(datetimeformat1),
|
||||
"AAS",
|
||||
"TIMER",
|
||||
weekly.Description,
|
||||
weekly.Soundpath,
|
||||
weekly.BroadcastZones,
|
||||
1.toUInt(),
|
||||
weekly.Language
|
||||
)
|
||||
db.queuetableDB.Add(qt)
|
||||
return
|
||||
|
||||
}
|
||||
// check daily schedule
|
||||
@@ -833,7 +873,19 @@ class MainExtension01 {
|
||||
it.Day == ScheduleDay.Everyday.name
|
||||
}
|
||||
if (daily != null) {
|
||||
// TODO Masukin ke queue table sebagai schedule daily
|
||||
val qt = QueueTable(
|
||||
0u,
|
||||
LocalDateTime.now().format(datetimeformat1),
|
||||
"AAS",
|
||||
"TIMER",
|
||||
daily.Description,
|
||||
daily.Soundpath,
|
||||
daily.BroadcastZones,
|
||||
1.toUInt(),
|
||||
daily.Language
|
||||
)
|
||||
db.queuetableDB.Add(qt)
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,14 @@ class AudioPlayer (var samplingrate: Int) {
|
||||
return result
|
||||
}
|
||||
|
||||
fun WavWriter(data: ByteArray, target: String, callback: BiConsumer<Boolean, String>) {
|
||||
val source = AudioFileInfo()
|
||||
source.bytes = data
|
||||
source.fileName = "In-Memory Data"
|
||||
val sources = listOf(source)
|
||||
WavWriter(sources, target, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the audio data from the sources to a WAV file.
|
||||
* @param sources List of AudioFileInfo objects containing the audio data to write.
|
||||
|
||||
79
src/audio/UDPReceiver.kt
Normal file
79
src/audio/UDPReceiver.kt
Normal file
@@ -0,0 +1,79 @@
|
||||
package audio
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.net.DatagramSocket
|
||||
import java.util.function.Consumer
|
||||
|
||||
@Suppress("unused")
|
||||
/**
|
||||
* UDPReceiver is a class that listens for UDP packets on a specified port.
|
||||
* It is designed to run in a separate thread and can be stopped when no longer needed.
|
||||
* @param portnumber The port to listen for incoming UDP packets (default is 5002
|
||||
*/
|
||||
class UDPReceiver(val portnumber: Int = 5002) {
|
||||
private lateinit var socket : DatagramSocket
|
||||
private var isRunning = false
|
||||
private val dataCallback = mutableMapOf<String, Consumer<ByteArray>>()
|
||||
|
||||
/**
|
||||
* Start listening for UDP packets on the specified port.
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
fun Start() : Boolean{
|
||||
return try {
|
||||
socket = DatagramSocket(portnumber)
|
||||
isRunning = true
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
while(isRunning){
|
||||
try {
|
||||
val buffer = ByteArray(2048)
|
||||
val packet = java.net.DatagramPacket(buffer, buffer.size)
|
||||
socket.receive(packet)
|
||||
val data = ByteArray(packet.length)
|
||||
System.arraycopy(packet.data, 0, data, 0, packet.length)
|
||||
dataCallback[packet.address.hostAddress].let {
|
||||
it?.accept(data)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
if (isRunning) {
|
||||
println("Error receiving UDP packet: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback function to be called when data is received from the specified IP address.
|
||||
* @param ipaddress The IP address to listen for incoming UDP packets.
|
||||
* @param callback A callback function that will be called when data is received from the specified IP address.
|
||||
*/
|
||||
fun RequestDataFrom(ipaddress: String, callback: Consumer<ByteArray>){
|
||||
dataCallback[ipaddress] = callback
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the callback function for the specified IP address.
|
||||
* @param ipaddress The IP address to stop listening for incoming UDP packets.
|
||||
*/
|
||||
fun StopRequestDataFrom(ipaddress: String){
|
||||
dataCallback.remove(ipaddress)
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening for UDP packets and close the socket.
|
||||
*/
|
||||
fun Stop(){
|
||||
if (isRunning){
|
||||
isRunning = false
|
||||
socket.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import java.net.InetSocketAddress
|
||||
import java.util.function.BiConsumer
|
||||
import java.util.function.Consumer
|
||||
|
||||
@Deprecated("Sepertinya gak jadi pake")
|
||||
@Suppress("unused")
|
||||
/**
|
||||
* UDPReceiverToFile is a class that listens for UDP packets on a specified address and port
|
||||
|
||||
@@ -13,6 +13,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Deprecated("Sepertinya gak jadi pake ini")
|
||||
@Suppress("unused")
|
||||
class UDPSenderFromFile(val fileName: String, val bytesPerPackage: Int=1024, targetIP: Array<String>, targetPort: Int ) {
|
||||
val bass: Bass = Bass.Instance
|
||||
|
||||
@@ -9,11 +9,11 @@ import java.util.function.Consumer
|
||||
|
||||
@Suppress("unused")
|
||||
class TCP_Barix_Command_Server {
|
||||
private var tcpserver: ServerSocket? = null
|
||||
private var job: Job? = null
|
||||
lateinit var tcpserver: ServerSocket
|
||||
lateinit var job: Job
|
||||
private val socketMap = mutableMapOf<String, Socket>()
|
||||
|
||||
private val regex = """\$\\"STATUSBARIX;(\d+);(\d+);?(\d)?\\"$"""
|
||||
private val regex = """STATUSBARIX;(\d+);(\d+);?(\d)?"""
|
||||
private val pattern = Regex(regex)
|
||||
|
||||
/**
|
||||
@@ -27,51 +27,44 @@ class TCP_Barix_Command_Server {
|
||||
val tcp = ServerSocket(port)
|
||||
tcpserver = tcp
|
||||
job = CoroutineScope(Dispatchers.IO).launch {
|
||||
Logger.info { "TCP server started" }
|
||||
Logger.info { "TCP StreamerOutput server started on port $port" }
|
||||
while (isActive) {
|
||||
if (tcpserver?.isClosed == true) break
|
||||
if (tcpserver.isClosed) break
|
||||
try {
|
||||
tcpserver?.accept().use { socket ->
|
||||
{
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
if (socket != null) {
|
||||
val key : String = socket.inetAddress.hostAddress+":"+socket.port
|
||||
socketMap[key] = socket
|
||||
Logger.info { "Start communicating with $key" }
|
||||
socket.getInputStream().use { din ->
|
||||
{
|
||||
while (isActive) {
|
||||
if (din.available()>0){
|
||||
val bb = ByteArray(din.available())
|
||||
din.read(bb)
|
||||
// B4A format, 4 bytes di depan adalah size
|
||||
val str = String(bb)
|
||||
if (ValidString(str)) {
|
||||
// Valid command from Barix is in format $"STATUSBARIX;VU;BuffRemain;StatusData"$
|
||||
pattern.find(str)?.let { matchResult ->
|
||||
val (vu, buffremain, statusdata) = matchResult.destructured
|
||||
val status = BarixStatus(
|
||||
socket.inetAddress.hostAddress,
|
||||
vu.toInt(),
|
||||
buffremain.toInt(),
|
||||
statusdata.toIntOrNull() ?: 0
|
||||
)
|
||||
Logger.info { "Received valid command from $key : $status" }
|
||||
cb.accept(status)
|
||||
} ?: run {
|
||||
Logger.warn { "Invalid command format from $key : $str" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Logger.info { "Finished communicating with $key" }
|
||||
socketMap.remove(key)
|
||||
}
|
||||
|
||||
val socket = tcpserver.accept()
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val key : String = socket.inetAddress.hostAddress
|
||||
socketMap[key] = socket
|
||||
Logger.info { "Start communicating with Streamer Output with IP : $key" }
|
||||
val din = socket.getInputStream()
|
||||
while (isActive) {
|
||||
if (din.available()>0){
|
||||
val bb = ByteArray(din.available())
|
||||
din.read(bb)
|
||||
// B4A format, 4 bytes di depan adalah size
|
||||
val str = String(bb, 4, bb.size - 4)
|
||||
if (ValidString(str)) {
|
||||
// Valid command from Barix is in format $"STATUSBARIX;VU;BuffRemain;StatusData"$
|
||||
pattern.find(str)?.let { matchResult ->
|
||||
val (vu, buffremain, statusdata) = matchResult.destructured
|
||||
val status = BarixStatus(
|
||||
socket.inetAddress.hostAddress,
|
||||
vu.toInt(),
|
||||
buffremain.toInt(),
|
||||
statusdata.toIntOrNull() ?: 0
|
||||
)
|
||||
//Logger.info { "Received valid command from $key : $status" }
|
||||
cb.accept(status)
|
||||
} ?: run {
|
||||
Logger.warn { "Invalid command format from $key : $str" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Logger.info { "Finished communicating with Streamer Output with IP $key" }
|
||||
socketMap.remove(key)
|
||||
|
||||
}
|
||||
|
||||
} catch (ex: Exception) {
|
||||
@@ -94,20 +87,18 @@ class TCP_Barix_Command_Server {
|
||||
*/
|
||||
fun StopTcpCommand(): Boolean {
|
||||
try {
|
||||
tcpserver?.close()
|
||||
tcpserver.close()
|
||||
runBlocking {
|
||||
socketMap.values.forEach {
|
||||
it.close()
|
||||
}
|
||||
socketMap.clear()
|
||||
job?.join()
|
||||
job.join()
|
||||
}
|
||||
Logger.info { "StopTcpCommand success" }
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
Logger.error { "Failed to StopTcpServer, Message : ${e.message}" }
|
||||
} finally {
|
||||
tcpserver = null
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
45
src/commandServer/PagingJob.kt
Normal file
45
src/commandServer/PagingJob.kt
Normal file
@@ -0,0 +1,45 @@
|
||||
package commandServer
|
||||
|
||||
import codes.Somecodes.Companion.PagingResult_directory
|
||||
import codes.Somecodes.Companion.filenameformat
|
||||
import org.tinylog.Logger
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.nio.file.Path
|
||||
import java.time.LocalDateTime
|
||||
|
||||
/**
|
||||
* Class to handle a paging job, storing incoming audio data and metadata.
|
||||
* @param fromIP The IP address from which the paging data is received.
|
||||
* @param broadcastzones The zones to which the paging is broadcasted, is a semicolon-separated string.
|
||||
*/
|
||||
class PagingJob(val fromIP: String, val broadcastzones: String) {
|
||||
val filePath : Path = PagingResult_directory.resolve(LocalDateTime.now().format(filenameformat)+"_RAW.wav")
|
||||
private val bos : ByteArrayOutputStream = ByteArrayOutputStream()
|
||||
var totalBytesReceived = 0; private set
|
||||
var isRunning = true; private set
|
||||
|
||||
|
||||
/**
|
||||
* Adds incoming audio data to the job.
|
||||
* @param data The byte array containing audio data.
|
||||
* @param length The number of bytes to write from the data array.
|
||||
*/
|
||||
fun addData(data: ByteArray, length: Int) {
|
||||
Logger.info{"PagingJob from $fromIP, zones: $broadcastzones, received $length bytes"}
|
||||
bos.write(data, 0, length)
|
||||
totalBytesReceived += length
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the accumulated audio data as a byte array.
|
||||
* @return A byte array containing all received audio data.
|
||||
*/
|
||||
fun GetData(): ByteArray {
|
||||
return bos.toByteArray()
|
||||
}
|
||||
|
||||
fun Close(){
|
||||
bos.close()
|
||||
isRunning = false
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,13 @@
|
||||
package commandServer
|
||||
|
||||
import audioPlayer
|
||||
import codes.Somecodes.Companion.ValidString
|
||||
import codes.Somecodes.Companion.datetimeformat1
|
||||
import content.Language
|
||||
import database.Messagebank
|
||||
import database.QueuePaging
|
||||
import database.QueueTable
|
||||
import database.Soundbank
|
||||
import db
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -9,19 +16,23 @@ import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.tinylog.Logger
|
||||
import udpreceiver
|
||||
import java.net.ServerSocket
|
||||
import java.net.Socket
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.time.LocalDateTime
|
||||
import java.util.function.Consumer
|
||||
import kotlin.io.path.absolutePathString
|
||||
|
||||
@Suppress("unused")
|
||||
class TCP_Android_Command_Server {
|
||||
private var tcpserver: ServerSocket? = null
|
||||
private var job: Job? = null
|
||||
lateinit var tcpserver: ServerSocket
|
||||
lateinit var job: Job
|
||||
private val socketMap = mutableMapOf<String, Socket>()
|
||||
lateinit var logcb: Consumer<String>
|
||||
private val listUserLogin = mutableListOf<userLogin>()
|
||||
private val listOnGoingPaging = mutableMapOf<String, PagingJob>()
|
||||
|
||||
/**
|
||||
* Start TCP Command Server
|
||||
@@ -35,15 +46,16 @@ class TCP_Android_Command_Server {
|
||||
val tcp = ServerSocket(port)
|
||||
tcpserver = tcp
|
||||
job = CoroutineScope(Dispatchers.IO).launch {
|
||||
Logger.info { "TCP server started" }
|
||||
Logger.info { "TCP Android server started on port $port" }
|
||||
while (isActive) {
|
||||
if (tcpserver?.isClosed == true) break
|
||||
if (tcpserver.isClosed) break
|
||||
try {
|
||||
tcpserver?.accept().use { socket ->
|
||||
tcpserver.accept().use { socket ->
|
||||
{
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
if (socket != null) {
|
||||
val key: String = socket.inetAddress.hostAddress + ":" + socket.port
|
||||
// key is IP address only
|
||||
val key: String = socket.inetAddress.hostAddress
|
||||
socketMap[key] = socket
|
||||
Logger.info { "Start communicating with $key" }
|
||||
socket.getInputStream().let { din ->
|
||||
@@ -108,23 +120,31 @@ class TCP_Android_Command_Server {
|
||||
return ByteArray(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Process command from Android client
|
||||
* @param key The client IP address
|
||||
* @param cmd The command string
|
||||
* @param cb Callback to send reply string
|
||||
*/
|
||||
private fun process_command(key: String, cmd: String, cb: Consumer<String>) {
|
||||
Logger.info { "Command from $key : $cmd" }
|
||||
val parts = cmd.split(";").map { it.trim() }.filter { it.isNotBlank() }.map { it.uppercase() }
|
||||
when (parts[0]) {
|
||||
"GETLOGIN" -> {
|
||||
// Android login request
|
||||
val username = parts.getOrElse(1) { "" }
|
||||
val password = parts.getOrElse(2) { "" }
|
||||
if (ValidString(username) && ValidString(password)) {
|
||||
if (db.userDB.List.any{it.username==username && it.password==password}) {
|
||||
cb.accept("LOGIN;TRUE@")
|
||||
val existing = listUserLogin.find { it.ip == key}
|
||||
if (existing!=null){
|
||||
existing.username = username
|
||||
} else{
|
||||
listUserLogin.add(userLogin(key, username))
|
||||
}
|
||||
cb.accept("LOGIN;TRUE@")
|
||||
logcb.accept("Android Login success from $key as $username")
|
||||
return
|
||||
} else {
|
||||
logcb.accept("Android Login failed from $key as $username")
|
||||
cb.accept("LOGIN;FALSE@")
|
||||
@@ -135,32 +155,252 @@ class TCP_Android_Command_Server {
|
||||
}
|
||||
}
|
||||
|
||||
"PCMFILE_START" -> {
|
||||
// TODO read coding here
|
||||
"PCMFILE_START","STARTPAGINGAND" -> {
|
||||
val zones = parts.getOrElse(3) { "" }.replace(",",";")
|
||||
if (ValidString(zones)){
|
||||
// create pagingjob
|
||||
val pj = PagingJob(key, zones)
|
||||
// masukin ke list
|
||||
listOnGoingPaging[key] = pj
|
||||
|
||||
// start minta data dari udpreceiver
|
||||
udpreceiver.RequestDataFrom(key){
|
||||
// push data ke paging job
|
||||
pj.addData(it, it.size)
|
||||
}
|
||||
logcb.accept("Paging started from Android $key")
|
||||
cb.accept(parts[0]+";OK@")
|
||||
return
|
||||
} else logcb.accept("Paging start from Android $key failed, empty zones")
|
||||
cb.accept(parts[0]+";NG@")
|
||||
}
|
||||
|
||||
"PCMFILE_STOP" -> {
|
||||
// TODO read coding here
|
||||
}
|
||||
"PCMFILE_STOP","STOPPAGINGAND" -> {
|
||||
val pj = listOnGoingPaging[key]
|
||||
if (pj!=null){
|
||||
listOnGoingPaging.remove(key)
|
||||
udpreceiver.StopRequestDataFrom(key)
|
||||
logcb.accept("Paging stopped from Android $key")
|
||||
cb.accept(parts[0]+";OK@")
|
||||
// get remaining data
|
||||
val data = pj.GetData()
|
||||
pj.Close()
|
||||
audioPlayer.WavWriter(data, pj.filePath.absolutePathString()){
|
||||
success, message ->
|
||||
if (success){
|
||||
// insert to paging queue
|
||||
val qp = QueuePaging(
|
||||
0u,
|
||||
LocalDateTime.now().format(datetimeformat1),
|
||||
"PAGING",
|
||||
"NORMAL",
|
||||
pj.filePath.absolutePathString(),
|
||||
pj.broadcastzones
|
||||
)
|
||||
if (db.queuepagingDB.Add(qp)){
|
||||
logcb.accept("Paging audio inserted to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}")
|
||||
cb.accept(parts[0]+";OK@")
|
||||
} else {
|
||||
logcb.accept("Failed to insert paging audio to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}")
|
||||
cb.accept(parts[0]+";NG@")
|
||||
}
|
||||
|
||||
"STARTPAGINGAND" -> {
|
||||
// TODO read coding here
|
||||
}
|
||||
} else {
|
||||
logcb.accept("Failed to write paging audio to file ${pj.filePath.absolutePathString()}, Message : $message")
|
||||
cb.accept(parts[0]+";NG@")
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
logcb.accept("Paging stop from Android $key failed, no ongoing paging")
|
||||
cb.accept(parts[0]+";NG@")
|
||||
}
|
||||
|
||||
"STOPPAGINGAND" -> {
|
||||
// TODO read coding here
|
||||
}
|
||||
|
||||
"CANCELPAGINGAND" -> {
|
||||
// TODO read coding here
|
||||
val pj = listOnGoingPaging[key]
|
||||
if (pj!=null){
|
||||
pj.Close()
|
||||
listOnGoingPaging.remove(key)
|
||||
udpreceiver.StopRequestDataFrom(key)
|
||||
logcb.accept("Paging from Android $key cancelled")
|
||||
cb.accept("CANCELPAGINGAND;OK@")
|
||||
return
|
||||
} else logcb.accept("Paging cancel from Android $key failed, no ongoing paging")
|
||||
cb.accept("CANCELPAGINGAND;NG@")
|
||||
|
||||
}
|
||||
|
||||
"STARTINITIALIZE" -> {
|
||||
// TODO read coding here
|
||||
// pengiriman variabel ke Android
|
||||
val username = parts.getOrElse(1) { "" }
|
||||
if (ValidString(username)){
|
||||
val userlogin = listUserLogin.find { it.username == username }
|
||||
if (userlogin != null){
|
||||
val userdb = db.userDB.List.find { it.username == username }
|
||||
if (userdb != null){
|
||||
val result = StringBuilder()
|
||||
result.append("ZONE")
|
||||
userdb.broadcastzones.split(";").map { it.trim() }.filter { it.isNotBlank() }.forEach {
|
||||
result.append(";")
|
||||
result.append(it)
|
||||
}
|
||||
result.append("@")
|
||||
val VARMESSAGES = mutableListOf<Messagebank>()
|
||||
userdb.messagebank_ann_id
|
||||
// messagebank_ann_id adalah rentengan ANN_ID (digit) yang dipisah dengan ;
|
||||
.split(";")
|
||||
// trim dulu
|
||||
.map { it.trim() }
|
||||
// bukan string kosong antar dua tanda ;
|
||||
.filter { it.isNotBlank() }
|
||||
// beneran digit semua
|
||||
.filter { xx -> xx.all{it.isDigit()} }
|
||||
// iterasi setiap ANN_ID
|
||||
.forEach { annid ->
|
||||
// masukin ke VARMESSAGES yang unik secara ANN_ID dan Language
|
||||
val xx = db.messageDB.List
|
||||
.filter{ it.ANN_ID == annid.toUInt() }
|
||||
.distinctBy { it.Language }
|
||||
VARMESSAGES.addAll(xx)
|
||||
}
|
||||
result.append("MSGTOTAL;").append(VARMESSAGES.size).append("@")
|
||||
// VAR AP TOTAL
|
||||
val VARAPTOTAL = mutableListOf<Soundbank>()
|
||||
val sb_split = userdb.soundbank_tags.split(";").map { it.trim() }.filter { it.isNotBlank() }
|
||||
sb_split.forEach {
|
||||
val sb = db.Find_Soundbank_AirplaneName(it).firstOrNull()
|
||||
if (sb != null) VARAPTOTAL.add(sb)
|
||||
}
|
||||
result.append("VARAPTOTAL;").append(VARAPTOTAL.size).append("@")
|
||||
// VAR CITY TOTAL
|
||||
val VARCITYTOTAL = mutableListOf<Soundbank>()
|
||||
sb_split.forEach {
|
||||
val sb = db.Find_Soundbank_City(it).firstOrNull()
|
||||
if (sb != null) VARCITYTOTAL.add(sb)
|
||||
}
|
||||
result.append("VARCITYTOTAL;").append(VARCITYTOTAL.size).append("@")
|
||||
// VAR PLACES TOTAL
|
||||
val VARPLACESTOTAL = mutableListOf<Soundbank>()
|
||||
sb_split.forEach {
|
||||
val sb = db.Find_Soundbank_Places(it).firstOrNull()
|
||||
if (sb != null) VARPLACESTOTAL.add(sb)
|
||||
}
|
||||
result.append("VARPLACESTOTAL;").append(VARPLACESTOTAL.size).append("@")
|
||||
// VAR SHALAT TOTAL
|
||||
val VARSHALATTOTAL = mutableListOf<Soundbank>()
|
||||
sb_split.forEach {
|
||||
val sb = db.Find_Soundbank_Shalat(it).firstOrNull()
|
||||
if (sb != null) VARSHALATTOTAL.add(sb)
|
||||
}
|
||||
result.append("VARSHALATTOTAL;").append(VARSHALATTOTAL.size).append("@")
|
||||
// VAR SEQUENCE TOTAL
|
||||
val VARSEQUENCETOTAL = mutableListOf<Soundbank>()
|
||||
sb_split.forEach {
|
||||
val sb = db.Find_Soundbank_Sequence(it).firstOrNull()
|
||||
if (sb != null) VARSEQUENCETOTAL.add(sb)
|
||||
}
|
||||
// VAR REASON TOTAL
|
||||
val VARREASONTOTAL = mutableListOf<Soundbank>()
|
||||
sb_split.forEach {
|
||||
val sb = db.Find_Soundbank_Reason(it).firstOrNull()
|
||||
if (sb != null) VARREASONTOTAL.add(sb)
|
||||
}
|
||||
result.append("VARREASONTOTAL;").append(VARREASONTOTAL.size).append("@")
|
||||
// VAR PROCEDURE TOTAL
|
||||
val VARPROCEDURETOTAL = mutableListOf<Soundbank>()
|
||||
sb_split.forEach {
|
||||
val sb = db.Find_Soundbank_Procedure(it).firstOrNull()
|
||||
if (sb != null) VARPROCEDURETOTAL.add(sb)
|
||||
}
|
||||
result.append("VARPROCEDURETOTAL;").append(VARPROCEDURETOTAL.size).append("@")
|
||||
// send to sender
|
||||
cb.accept(result.toString())
|
||||
|
||||
result.clear()
|
||||
|
||||
//Append MSG, for Android only Indonesia and English
|
||||
VARMESSAGES.groupBy { it.ANN_ID }.forEach { (ann_id, value) ->
|
||||
result.append("MSG;").append(ann_id)
|
||||
result.append(";")
|
||||
value.find { it.Language== Language.INDONESIA.name }?.let {result.append(it.Message_Detail)} ?: result.append("NA")
|
||||
result.append(";")
|
||||
value.find {it.Language== Language.ENGLISH.name }?.let {result.append(it.Message_Detail)} ?: result.append("NA")
|
||||
result.append("@")
|
||||
}
|
||||
|
||||
// append VARAP
|
||||
VARAPTOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank ->
|
||||
result.append("VARAP;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@")
|
||||
}
|
||||
// append VARCITY
|
||||
VARCITYTOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank ->
|
||||
result.append("VARCITY;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@")
|
||||
}
|
||||
// append VARPLACES
|
||||
VARPLACESTOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank ->
|
||||
result.append("VARPLACES;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@")
|
||||
}
|
||||
// append VARSHALAT
|
||||
VARSHALATTOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank ->
|
||||
result.append("VARSHALAT;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@")
|
||||
}
|
||||
// append VARSEQUENCE
|
||||
VARSEQUENCETOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank ->
|
||||
result.append("VARSEQUENCE;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@")
|
||||
}
|
||||
// append VARREASON
|
||||
VARREASONTOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank ->
|
||||
result.append("VARREASON;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@")
|
||||
}
|
||||
// append VARPROCEDURE
|
||||
VARPROCEDURETOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank ->
|
||||
result.append("VARPROCEDURE;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@")
|
||||
}
|
||||
// send to sender
|
||||
cb.accept(result.toString())
|
||||
logcb.accept("All variables sent to $key with username $username")
|
||||
|
||||
return
|
||||
} else logcb.accept("STARTINITIALIZE failed from $key with username $username not found in userDB")
|
||||
} else logcb.accept("STARTINITIALIZE failed from $key with unregistered username $username")
|
||||
} else logcb.accept("STARTINITIALIZE failed from $key with empty username")
|
||||
cb.accept("STARTINITIALIZE;FALSE@")
|
||||
}
|
||||
|
||||
"BROADCASTAND" -> {
|
||||
// TODO read coding here
|
||||
// semi auto dari android, masukin ke queue table
|
||||
val desc = parts.getOrElse(1) { "" }
|
||||
val lang = parts.getOrElse(2) { "" }.replace(",",";")
|
||||
val tags = parts.getOrElse(3) { "" }.replace(",",";")
|
||||
val zone = parts.getOrElse(4) { "" }.replace(",",";")
|
||||
if (ValidString(desc)){
|
||||
if (ValidString(lang)){
|
||||
if (ValidString(tags)){
|
||||
if (ValidString(zone)){
|
||||
val qt = QueueTable(
|
||||
0u,
|
||||
LocalDateTime.now().format(datetimeformat1),
|
||||
"ANDROID",
|
||||
"SOUNDBANK",
|
||||
desc,
|
||||
tags,
|
||||
zone,
|
||||
1u,
|
||||
lang
|
||||
)
|
||||
if (db.queuetableDB.Add(qt)){
|
||||
logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} inserted. Message: $desc;$lang;$tags;$zone")
|
||||
cb.accept("BROADCASTAND;OK@")
|
||||
return
|
||||
} else logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} failed, cannot add to queue table")
|
||||
} else logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} failed, empty zone")
|
||||
} else logcb.accept("Broadcsast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} failed, empty tags")
|
||||
} else logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} failed, empty language")
|
||||
} else logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} failed, empty description")
|
||||
cb.accept("NG@")
|
||||
|
||||
}
|
||||
|
||||
else -> {
|
||||
@@ -176,20 +416,18 @@ class TCP_Android_Command_Server {
|
||||
*/
|
||||
fun StopTcpCommand(): Boolean {
|
||||
try {
|
||||
tcpserver?.close()
|
||||
tcpserver.close()
|
||||
runBlocking {
|
||||
socketMap.values.forEach {
|
||||
it.close()
|
||||
}
|
||||
socketMap.clear()
|
||||
job?.join()
|
||||
job.join()
|
||||
}
|
||||
Logger.info { "StopTcpCommand success" }
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
Logger.error { "Failed to StopTcpServer, Message : ${e.message}" }
|
||||
} finally {
|
||||
tcpserver = null
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ import java.util.function.Consumer
|
||||
|
||||
@Suppress("unused")
|
||||
class TCP_PC_Command_Server {
|
||||
private var tcpserver: ServerSocket? = null
|
||||
private var job: Job? = null
|
||||
lateinit var tcpserver: ServerSocket
|
||||
lateinit var job: Job
|
||||
private val socketMap = mutableMapOf<String, Socket>()
|
||||
|
||||
/**
|
||||
@@ -31,11 +31,11 @@ class TCP_PC_Command_Server {
|
||||
job = CoroutineScope(Dispatchers.IO).launch {
|
||||
Logger.info { "TCP server started" }
|
||||
while (isActive) {
|
||||
if (tcpserver?.isClosed == true) break
|
||||
if (tcpserver.isClosed) break
|
||||
try {
|
||||
tcpserver?.accept().use { socket ->
|
||||
tcpserver.accept().use { socket ->
|
||||
{
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
if (socket != null) {
|
||||
val key : String = socket.inetAddress.hostAddress+":"+socket.port
|
||||
socketMap[key] = socket
|
||||
@@ -81,20 +81,18 @@ class TCP_PC_Command_Server {
|
||||
*/
|
||||
fun StopTcpCommand(): Boolean {
|
||||
try {
|
||||
tcpserver?.close()
|
||||
tcpserver.close()
|
||||
runBlocking {
|
||||
socketMap.values.forEach {
|
||||
it.close()
|
||||
}
|
||||
socketMap.clear()
|
||||
job?.join()
|
||||
job.join()
|
||||
}
|
||||
Logger.info { "StopTcpCommand success" }
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
Logger.error { "Failed to StopTcpServer, Message : ${e.message}" }
|
||||
} finally {
|
||||
tcpserver = null
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -13,5 +13,6 @@ enum class Category(name: String) {
|
||||
Year("Year"),
|
||||
Birthday("Birthday"),
|
||||
Reason("Reason"),
|
||||
Sequence("Sequence"),
|
||||
Procedure("Procedure");
|
||||
}
|
||||
@@ -20,4 +20,8 @@ data class Log(
|
||||
return Log(0u, date, time, machine, description)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() : String {
|
||||
return "$datenya $timenya [$machine] $description"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,17 @@ package database
|
||||
|
||||
import codes.Somecodes.Companion.ValiDateForLogHtml
|
||||
import codes.Somecodes.Companion.toJsonString
|
||||
import content.Category
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import max_channel
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook
|
||||
import org.tinylog.Logger
|
||||
import java.sql.Connection
|
||||
import java.sql.DriverManager
|
||||
import java.util.function.Consumer
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* A class to manage a connection to a MariaDB database.
|
||||
@@ -1463,7 +1466,7 @@ class MariaDB(
|
||||
val countResult = statement?.executeQuery("SELECT COUNT(*) AS count FROM ${super.dbName}")
|
||||
if (countResult?.next() == true) {
|
||||
val count = countResult.getInt("count")
|
||||
if (count == 0) {
|
||||
if (count < max_channel) {
|
||||
Logger.info("SoundChannel table is empty, populating with default channels" as Any)
|
||||
Clear()
|
||||
}
|
||||
@@ -1577,9 +1580,9 @@ class MariaDB(
|
||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||
Logger.info("${super.dbName} table cleared" as Any)
|
||||
List.clear()
|
||||
// create new rows from 1 to 64 with description "Channel 01" to "Channel 64" and empty ip
|
||||
for (i in 1..64) {
|
||||
val channel = String.format("Channel %02d", i)
|
||||
// create new rows from 1 to 64 with description "Channel 1" to "Channel 64" and empty ip
|
||||
for (i in 1..max_channel) {
|
||||
val channel = String.format("Channel %d", i)
|
||||
val insertStatement =
|
||||
connection.prepareStatement("INSERT INTO ${super.dbName} (channel, ip) VALUES (?, ?)")
|
||||
insertStatement?.setString(1, channel)
|
||||
@@ -1727,13 +1730,13 @@ class MariaDB(
|
||||
statement?.setString(4, data.description)
|
||||
val rowsAffected = statement?.executeUpdate()
|
||||
if (rowsAffected != null && rowsAffected > 0) {
|
||||
Logger.info("Log added: [$data.datenya $data.timenya] [$data.machine] $data.description" as Any)
|
||||
Logger.info{"Log added : $data"}
|
||||
return true
|
||||
} else {
|
||||
Logger.warn("No log entry added for: [$data.datenya $data.timenya] [$data.machine] $data.description" as Any)
|
||||
Logger.warn{"Failed to add log entry : $data"}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Logger.error("Error adding log entry: ${e.message}" as Any)
|
||||
Logger.error{"Error adding log entry: ${e.message}"}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -2239,6 +2242,63 @@ class MariaDB(
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all city soundbank by tag
|
||||
* @param tag The tags to search for
|
||||
* @return a list of Soundbank with Category City and matching tag
|
||||
*/
|
||||
fun Find_Soundbank_City(tag: String) : List<Soundbank>{
|
||||
val lowerTag = tag.lowercase()
|
||||
return soundDB.List
|
||||
.filter{ it.Category== Category.City.name }
|
||||
.filter { it.TAG.lowercase()==lowerTag}
|
||||
}
|
||||
|
||||
fun Find_Soundbank_AirplaneName(tag: String) : List<Soundbank>{
|
||||
val lowerTag = tag.lowercase()
|
||||
return soundDB.List
|
||||
.filter{ it.Category== Category.Airplane_Name.name }
|
||||
.filter { it.TAG.lowercase()==lowerTag}
|
||||
}
|
||||
|
||||
fun Find_Soundbank_Places(tag: String) : List<Soundbank>{
|
||||
val lowerTag = tag.lowercase()
|
||||
return soundDB.List
|
||||
.filter{ it.Category== Category.Places.name }
|
||||
.filter { it.TAG.lowercase()==lowerTag}
|
||||
}
|
||||
|
||||
fun Find_Soundbank_Shalat(tag: String) : List<Soundbank>{
|
||||
val lowerTag = tag.lowercase()
|
||||
return soundDB.List
|
||||
.filter{ it.Category== Category.Shalat.name }
|
||||
.filter { it.TAG.lowercase()==lowerTag}
|
||||
}
|
||||
|
||||
fun Find_Soundbank_Sequence(tag: String) : List<Soundbank>{
|
||||
val lowerTag = tag.lowercase()
|
||||
|
||||
return soundDB.List
|
||||
.filter{ it.Category== Category.Sequence.name }
|
||||
.filter { it.TAG.lowercase()==lowerTag}
|
||||
}
|
||||
|
||||
fun Find_Soundbank_Reason(tag: String) : List<Soundbank>{
|
||||
val lowerTag = tag.lowercase()
|
||||
return soundDB.List
|
||||
.filter{ it.Category== Category.Reason.name }
|
||||
.filter { it.TAG.lowercase()==lowerTag}
|
||||
}
|
||||
|
||||
fun Find_Soundbank_Procedure(tag: String) : List<Soundbank> {
|
||||
val lowerTag = tag.lowercase()
|
||||
return soundDB.List
|
||||
.filter { it.Category == Category.Procedure.name }
|
||||
.filter { it.TAG.lowercase() == lowerTag }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
package database
|
||||
|
||||
@Suppress("unused")
|
||||
data class QueuePaging(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var BroadcastZones: String)
|
||||
data class QueuePaging(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var BroadcastZones: String){
|
||||
override fun toString(): String {
|
||||
return "QueuePaging(index=$index, Date_Time='$Date_Time', Source='$Source', Type='$Type', Message='$Message', BroadcastZones='$BroadcastZones')"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
package database
|
||||
|
||||
@Suppress("unused")
|
||||
data class QueueTable(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var SB_TAGS: String, var BroadcastZones: String, var Repeat: UInt, var Language: String)
|
||||
data class QueueTable(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var SB_TAGS: String, var BroadcastZones: String, var Repeat: UInt, var Language: String){
|
||||
|
||||
override fun toString(): String {
|
||||
return "QueueTable(index=$index, Date_Time='$Date_Time', Source='$Source', Type='$Type', Message='$Message', SB_TAGS='$SB_TAGS', BroadcastZones='$BroadcastZones', Repeat=$Repeat, Language='$Language')"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import content.Language
|
||||
import content.ScheduleDay
|
||||
import content.VoiceType
|
||||
import database.BroadcastZones
|
||||
import database.BroadcastZonesHtml
|
||||
import database.LanguageLink
|
||||
import database.MariaDB
|
||||
import database.Messagebank
|
||||
@@ -652,6 +651,9 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate schedulebank table")))
|
||||
}
|
||||
}
|
||||
post("Add"){
|
||||
// TODO add new schedule
|
||||
}
|
||||
delete("DeleteByIndex/{index}") {
|
||||
// delete by index
|
||||
val index = it.pathParam("index").toUIntOrNull()
|
||||
@@ -831,17 +833,17 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
path("BroadcastZones"){
|
||||
get("List") {
|
||||
// TODO : temporary, convert BroadcastZones to BroadcastZonesHtml, karena harus revisi javascript di Bootstrap Studio
|
||||
val newlist: ArrayList<BroadcastZonesHtml> = db.broadcastDB.List.map { xx ->
|
||||
BroadcastZonesHtml(
|
||||
xx.index,
|
||||
xx.description,
|
||||
xx.SoundChannel,
|
||||
xx.id,
|
||||
xx.bp
|
||||
)
|
||||
} as ArrayList<BroadcastZonesHtml>
|
||||
// val newlist: ArrayList<BroadcastZonesHtml> = db.broadcastDB.List.map { xx ->
|
||||
// BroadcastZonesHtml(
|
||||
// xx.index,
|
||||
// xx.description,
|
||||
// xx.SoundChannel,
|
||||
// xx.id,
|
||||
// xx.bp
|
||||
// )
|
||||
// } as ArrayList<BroadcastZonesHtml>
|
||||
|
||||
it.result(MariaDB.ArrayListtoString(newlist))
|
||||
it.result(MariaDB.ArrayListtoString(db.broadcastDB.List))
|
||||
}
|
||||
delete("List"){
|
||||
// truncate broadcast zones table
|
||||
|
||||
Reference in New Issue
Block a user