Compare commits

...

18 Commits

Author SHA1 Message Date
1563e233d6 commit 14/10/2025 WebApp Add send language for ScheduleBank 2025-10-15 13:32:08 +07:00
a53270aaed commit 14/10/2025 WebApp Add send language for ScheduleBank 2025-10-14 16:26:14 +07:00
d2a2626fd5 Merge remote-tracking branch 'origin/master' into feature-webapp 2025-10-14 14:20:08 +07:00
de54d142ae commit 14/10/2025
Soundbank Menu beres
2025-10-14 13:22:48 +07:00
8c49bb827f Merge remote-tracking branch 'origin/master' into feature-webapp
# Conflicts:
#	src/web/WebApp.kt
2025-10-14 09:11:36 +07:00
b15470845e Merge remote-tracking branch 'origin/master' into feature-webapp
# Conflicts:
#	src/web/WebApp.kt
2025-10-14 09:07:17 +07:00
5f57e1bf2e commit 14/10/2025
combine with coding steph
2025-10-14 08:50:05 +07:00
a133f9a170 commit 14/10/2025
combine with coding steph
2025-10-14 08:21:07 +07:00
1d2c8d1307 commit 08/10/2025 WebApp 2025-10-14 07:21:52 +07:00
e426522380 commit 08/10/2025 WebApp 2025-10-13 16:16:15 +07:00
110e6d5b12 commit 13/10/2025
Soundbank menu
2025-10-13 15:55:09 +07:00
470f61c79d Merge branch 'master' of https://gitea.rdkartono.my.id/rdkartono/AAS_NewGeneration into feature-webapp
# Conflicts:
#	src/web/WebApp.kt
2025-10-13 10:52:21 +07:00
e78fb932b2 commit 10/10/2025
Restrukturisasi soundbank path di database
2025-10-10 16:21:28 +07:00
e0cdf74dec commit 10/10/2025
Restrukturisasi soundbank path di database
2025-10-10 16:21:13 +07:00
41d6dd7f47 commit 10/10/2025
Broadcast Zones and Sound Channels
2025-10-10 15:01:09 +07:00
fdc7556dd7 commit 10/10/2025
User Management
2025-10-10 10:03:14 +07:00
7f647fe9c3 commit 10/10/2025
User Management
2025-10-10 09:52:11 +07:00
d549aee42c commit 09/10/2025
User Management belum kelar
2025-10-09 15:49:03 +07:00
36 changed files with 2343 additions and 794 deletions

View File

@@ -0,0 +1,39 @@
:root, [data-bs-theme=light] {
--bs-primary: #0d6efd;
--bs-primary-rgb: 13,110,253;
--bs-primary-text-emphasis: #052C65;
--bs-primary-bg-subtle: #CFE2FF;
--bs-primary-border-subtle: #9EC5FE;
}
.btn-primary {
--bs-btn-color: #fff;
--bs-btn-bg: #0d6efd;
--bs-btn-border-color: #0d6efd;
--bs-btn-hover-color: #fff;
--bs-btn-hover-bg: #0B5ED7;
--bs-btn-hover-border-color: #0A58CA;
--bs-btn-focus-shadow-rgb: 219,233,255;
--bs-btn-active-color: #fff;
--bs-btn-active-bg: #0A58CA;
--bs-btn-active-border-color: #0A53BE;
--bs-btn-disabled-color: #fff;
--bs-btn-disabled-bg: #0d6efd;
--bs-btn-disabled-border-color: #0d6efd;
}
.btn-outline-primary {
--bs-btn-color: #0d6efd;
--bs-btn-border-color: #0d6efd;
--bs-btn-focus-shadow-rgb: 13,110,253;
--bs-btn-hover-color: #fff;
--bs-btn-hover-bg: #0d6efd;
--bs-btn-hover-border-color: #0d6efd;
--bs-btn-active-color: #fff;
--bs-btn-active-bg: #0d6efd;
--bs-btn-active-border-color: #0d6efd;
--bs-btn-disabled-color: #0d6efd;
--bs-btn-disabled-bg: transparent;
--bs-btn-disabled-border-color: #0d6efd;
}

629
html/webpage/assets/css/select2.min.css vendored Normal file
View File

@@ -0,0 +1,629 @@
.select2-container {
box-sizing: border-box;
display: inline-block;
margin: 0;
position: relative;
vertical-align: middle;
}
.select2-container .select2-selection--single {
box-sizing: border-box;
cursor: pointer;
display: block;
height: 28px;
user-select: none;
-webkit-user-select: none;
}
.select2-container .select2-selection--single .select2-selection__rendered {
display: block;
padding-left: 8px;
padding-right: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.select2-container .select2-selection--single .select2-selection__clear {
position: relative;
}
.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
padding-right: 8px;
padding-left: 20px;
}
.select2-container .select2-selection--multiple {
box-sizing: border-box;
cursor: pointer;
display: block;
min-height: 32px;
user-select: none;
-webkit-user-select: none;
}
.select2-container .select2-selection--multiple .select2-selection__rendered {
display: inline-block;
overflow: hidden;
padding-left: 8px;
text-overflow: ellipsis;
white-space: nowrap;
}
.select2-container .select2-search--inline {
float: left;
}
.select2-container .select2-search--inline .select2-search__field {
box-sizing: border-box;
border: none;
font-size: 100%;
margin-top: 5px;
padding: 0;
}
.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
-webkit-appearance: none;
}
.select2-dropdown {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
box-sizing: border-box;
display: block;
position: absolute;
left: -100000px;
width: 100%;
z-index: 1051;
}
.select2-results {
display: block;
}
.select2-results__options {
list-style: none;
margin: 0;
padding: 0;
}
.select2-results__option {
padding: 6px;
user-select: none;
-webkit-user-select: none;
}
.select2-results__option[aria-selected] {
cursor: pointer;
}
.select2-container--open .select2-dropdown {
left: 0;
}
.select2-container--open .select2-dropdown--above {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.select2-container--open .select2-dropdown--below {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.select2-search--dropdown {
display: block;
padding: 4px;
}
.select2-search--dropdown .select2-search__field {
padding: 4px;
width: 100%;
box-sizing: border-box;
}
.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
-webkit-appearance: none;
}
.select2-search--dropdown.select2-search--hide {
display: none;
}
.select2-close-mask {
border: 0;
margin: 0;
padding: 0;
display: block;
position: fixed;
left: 0;
top: 0;
min-height: 100%;
min-width: 100%;
height: auto;
width: auto;
opacity: 0;
z-index: 99;
background-color: #fff;
filter: alpha(opacity=0);
}
.select2-hidden-accessible {
border: 0 !important;
clip: rect(0 0 0 0) !important;
-webkit-clip-path: inset(50%) !important;
clip-path: inset(50%) !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
position: absolute !important;
width: 1px !important;
white-space: nowrap !important;
}
.select2-container--default .select2-selection--single {
background-color: #fff;
border: 1px solid #aaa;
border-radius: 4px;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
color: #444;
line-height: 28px;
}
.select2-container--default .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
}
.select2-container--default .select2-selection--single .select2-selection__placeholder {
color: #999;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px;
}
.select2-container--default .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0;
}
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left;
}
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
left: 1px;
right: auto;
}
.select2-container--default.select2-container--disabled .select2-selection--single {
background-color: #eee;
cursor: default;
}
.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
display: none;
}
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px;
}
.select2-container--default .select2-selection--multiple {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
cursor: text;
}
.select2-container--default .select2-selection--multiple .select2-selection__rendered {
box-sizing: border-box;
list-style: none;
margin: 0;
padding: 0 5px;
width: 100%;
}
.select2-container--default .select2-selection--multiple .select2-selection__rendered li {
list-style: none;
}
.select2-container--default .select2-selection--multiple .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin-top: 5px;
margin-right: 10px;
padding: 1px;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice {
background-color: #e4e4e4;
border: 1px solid #aaa;
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
color: #999;
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #333;
}
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline {
float: right;
}
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
margin-left: 5px;
margin-right: auto;
}
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto;
}
.select2-container--default.select2-container--focus .select2-selection--multiple {
border: solid black 1px;
outline: 0;
}
.select2-container--default.select2-container--disabled .select2-selection--multiple {
background-color: #eee;
cursor: default;
}
.select2-container--default.select2-container--disabled .select2-selection__choice__remove {
display: none;
}
.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.select2-container--default .select2-search--dropdown .select2-search__field {
border: 1px solid #aaa;
}
.select2-container--default .select2-search--inline .select2-search__field {
background: transparent;
border: none;
outline: 0;
box-shadow: none;
-webkit-appearance: textfield;
}
.select2-container--default .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto;
}
.select2-container--default .select2-results__option[role=group] {
padding: 0;
}
.select2-container--default .select2-results__option[aria-disabled=true] {
color: #999;
}
.select2-container--default .select2-results__option[aria-selected=true] {
background-color: #ddd;
}
.select2-container--default .select2-results__option .select2-results__option {
padding-left: 1em;
}
.select2-container--default .select2-results__option .select2-results__option .select2-results__group {
padding-left: 0;
}
.select2-container--default .select2-results__option .select2-results__option .select2-results__option {
margin-left: -1em;
padding-left: 2em;
}
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -2em;
padding-left: 3em;
}
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -3em;
padding-left: 4em;
}
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -4em;
padding-left: 5em;
}
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -5em;
padding-left: 6em;
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: #5897fb;
color: white;
}
.select2-container--default .select2-results__group {
cursor: default;
display: block;
padding: 6px;
}
.select2-container--classic .select2-selection--single {
background-color: #f7f7f7;
border: 1px solid #aaa;
border-radius: 4px;
outline: 0;
background-image: -webkit-linear-gradient(top, #fff 50%, #eee 100%);
background-image: -o-linear-gradient(top, #fff 50%, #eee 100%);
background-image: linear-gradient(to bottom, #fff 50%, #eee 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0);
}
.select2-container--classic .select2-selection--single:focus {
border: 1px solid #5897fb;
}
.select2-container--classic .select2-selection--single .select2-selection__rendered {
color: #444;
line-height: 28px;
}
.select2-container--classic .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin-right: 10px;
}
.select2-container--classic .select2-selection--single .select2-selection__placeholder {
color: #999;
}
.select2-container--classic .select2-selection--single .select2-selection__arrow {
background-color: #ddd;
border: none;
border-left: 1px solid #aaa;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px;
background-image: -webkit-linear-gradient(top, #eee 50%, #ccc 100%);
background-image: -o-linear-gradient(top, #eee 50%, #ccc 100%);
background-image: linear-gradient(to bottom, #eee 50%, #ccc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0);
}
.select2-container--classic .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0;
}
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left;
}
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
border: none;
border-right: 1px solid #aaa;
border-radius: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
left: 1px;
right: auto;
}
.select2-container--classic.select2-container--open .select2-selection--single {
border: 1px solid #5897fb;
}
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
background: transparent;
border: none;
}
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px;
}
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0;
background-image: -webkit-linear-gradient(top, #fff 0%, #eee 50%);
background-image: -o-linear-gradient(top, #fff 0%, #eee 50%);
background-image: linear-gradient(to bottom, #fff 0%, #eee 50%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0);
}
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
background-image: -webkit-linear-gradient(top, #eee 50%, #fff 100%);
background-image: -o-linear-gradient(top, #eee 50%, #fff 100%);
background-image: linear-gradient(to bottom, #eee 50%, #fff 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0);
}
.select2-container--classic .select2-selection--multiple {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
cursor: text;
outline: 0;
}
.select2-container--classic .select2-selection--multiple:focus {
border: 1px solid #5897fb;
}
.select2-container--classic .select2-selection--multiple .select2-selection__rendered {
list-style: none;
margin: 0;
padding: 0 5px;
}
.select2-container--classic .select2-selection--multiple .select2-selection__clear {
display: none;
}
.select2-container--classic .select2-selection--multiple .select2-selection__choice {
background-color: #e4e4e4;
border: 1px solid #aaa;
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px;
}
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
color: #888;
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px;
}
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #555;
}
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
float: right;
margin-left: 5px;
margin-right: auto;
}
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto;
}
.select2-container--classic.select2-container--open .select2-selection--multiple {
border: 1px solid #5897fb;
}
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.select2-container--classic .select2-search--dropdown .select2-search__field {
border: 1px solid #aaa;
outline: 0;
}
.select2-container--classic .select2-search--inline .select2-search__field {
outline: 0;
box-shadow: none;
}
.select2-container--classic .select2-dropdown {
background-color: #fff;
border: 1px solid transparent;
}
.select2-container--classic .select2-dropdown--above {
border-bottom: none;
}
.select2-container--classic .select2-dropdown--below {
border-top: none;
}
.select2-container--classic .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto;
}
.select2-container--classic .select2-results__option[role=group] {
padding: 0;
}
.select2-container--classic .select2-results__option[aria-disabled=true] {
color: grey;
}
.select2-container--classic .select2-results__option--highlighted[aria-selected] {
background-color: #3875d7;
color: #fff;
}
.select2-container--classic .select2-results__group {
cursor: default;
display: block;
padding: 6px;
}
.select2-container--classic.select2-container--open .select2-dropdown {
border-color: #5897fb;
}

View File

@@ -62,9 +62,9 @@ body {
.btn-round-basic:focus { .btn-round-basic:focus {
background-color: #f5f5f5; background-color: #f5f5f5;
border-radius: 20px; border-radius: 8px;
box-shadow: inset 4px 4px 10px #88a5bf7b, inset -4px -4px 10px #ffffff; box-shadow: inset 4px 4px 10px #88a5bf7b, inset -4px -4px 10px #ffffff;
color: #4d4d4d; /*color: #4d4d4d;*/
cursor: pointer; cursor: pointer;
font-size: 16px; font-size: 16px;
transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out;
@@ -72,7 +72,7 @@ body {
} }
.btn-round-basic { .btn-round-basic {
border-radius: 20px; border-radius: 08px;
box-shadow: rgba(136, 165, 191, 0.48) 6px 2px 16px 0px, rgba(255, 255, 255, 0.8) -6px -2px 16px 0px; box-shadow: rgba(136, 165, 191, 0.48) 6px 2px 16px 0px, rgba(255, 255, 255, 0.8) -6px -2px 16px 0px;
--bs-btn-hover-bg: #ffffff; --bs-btn-hover-bg: #ffffff;
} }
@@ -82,24 +82,24 @@ body {
box-shadow: inset 2px 2px 5px #bcbcbc, inset -2px -2px 5px #ffffff, 2px 2px 5px #bcbcbc, -2px -2px 5px #ffffff; box-shadow: inset 2px 2px 5px #bcbcbc, inset -2px -2px 5px #ffffff, 2px 2px 5px #bcbcbc, -2px -2px 5px #ffffff;
} }
.color-import { .color-import, .color-import:hover, .color-import:focus {
color: var(--bs-teal); color: var(--bs-teal);
} }
.color-remove { .color-remove, .color-remove:hover, .color-remove:focus {
color: var(--bs-danger); color: var(--bs-danger);
} }
.color-edit { .color-edit, .color-edit:hover, .color-edit:focus {
color: var(--bs-primary-text-emphasis); color: var(--bs-primary-text-emphasis);
} }
.color-add { .color-add, .color-add:hover, .color-add:focus {
color: var(--bs-primary); color: var(--bs-primary);
} }
.input-login { .input-login {
border-radius: 50px; border-radius: 8px;
box-shadow: rgba(9, 30, 66, 0.25) 0px 4px 8px -2px, rgba(9, 30, 66, 0.08) 0px 0px 0px 1px; box-shadow: rgba(9, 30, 66, 0.25) 0px 4px 8px -2px, rgba(9, 30, 66, 0.08) 0px 0px 0px 1px;
} }
@@ -227,25 +227,23 @@ nav-item:focus {
} }
.accordion-item.active { .accordion-item.active {
/*background: rgba(45, 53, 120, 0.15);*/ 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);*/ box-shadow: inset 4px 4px 10px rgba(45, 53, 120, 0.25), inset -4px -4px 10px rgba(255, 255, 255, 0.8);
} }
.bg-accordion { .bg-accordion {
border: white 2px; border: white 2px;
/*border-radius: 10px;*/ border-radius: 8px;
background: #f8f9fd; background: #f8f9fd;
} }
.card-channel { .card-channel {
border-radius: 12px; border-radius: 8px;
background: #ffffff; background: #ffffff;
backdrop-filter: blur(12px); backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
transition: transform 0.2s ease, box-shadow 0.2s ease; transition: transform 0.2s ease, box-shadow 0.2s ease;
border: #ffffff solid 2px !important; border: #ffffff solid 2px !important;
/*padding-left: 2px;*/
/*padding-right: 5px;*/
padding-top: 12px; padding-top: 12px;
padding-bottom: 12px; padding-bottom: 12px;
} }
@@ -255,3 +253,27 @@ nav-item:focus {
border: white solid 3px; border: white solid 3px;
} }
.progress-bar {
background-color: #172066;
}
table {
border-radius: 8px;
}
.bg-modal-body {
background-color: #f8f9fd;
}
.bg-header-modal {
background-color: #f0f2ff;
}
.pad-row-btn {
margin: 0.5rem;
}
.pad-2 {
border-radius: 0 !important;
}

View File

@@ -1,4 +1,17 @@
/**
* @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 ??= [];
/** /**
* Currently selected broadcast zone row in the table * Currently selected broadcast zone row in the table
@@ -6,6 +19,12 @@
*/ */
window.selectedBroadcastZoneRow = null; window.selectedBroadcastZoneRow = null;
/**
* List of sound channels available
* @type {String[]}
*/
window.SoundChannelList = []
/** /**
* Fill broadcast zone table body with values * Fill broadcast zone table body with values
* @param {BroadcastZone[]} vv values to fill * @param {BroadcastZone[]} vv values to fill
@@ -23,7 +42,7 @@ function fill_broadcastzonetablebody(vv) {
</tr>`; </tr>`;
$('#broadcastzonetablebody').append(row); $('#broadcastzonetablebody').append(row);
let $addedrow = $('#broadcastzonetablebody tr:last'); let $addedrow = $('#broadcastzonetablebody tr:last');
$addedrow.click(function () { $addedrow.off('click').on('click', function () {
if (window.selectedBroadcastZoneRow) { if (window.selectedBroadcastZoneRow) {
window.selectedBroadcastZoneRow.find('td').css('background-color', ''); window.selectedBroadcastZoneRow.find('td').css('background-color', '');
if (window.selectedBroadcastZoneRow.is($(this))) { if (window.selectedBroadcastZoneRow.is($(this))) {
@@ -42,7 +61,34 @@ function fill_broadcastzonetablebody(vv) {
$('#tablesize').text("Table Size: " + vv.length); $('#tablesize').text("Table Size: " + vv.length);
} }
/**
* Reload broadcast zones from server
* @param {String} APIURL API URL endpoint (default "BroadcastZones/")
*/
function reloadBroadcastZones(APIURL = "BroadcastZones/") {
window.BroadcastZoneList = [];
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
if (Array.isArray(okdata)) {
//console.log("reloadBroadcastZones : ", okdata)
window.BroadcastZoneList.push(...okdata);
fill_broadcastzonetablebody(window.BroadcastZoneList);
} else console.log("reloadBroadcastZones: okdata is not array");
}, (errdata) => {
alert("Error loading broadcast zones : " + errdata.message);
});
}
function fetchSoundChannels(APIURL = "SoundChannel/") {
window.SoundChannelList = [];
fetchAPI(APIURL + "SoundChannelDescriptions", "GET", {}, null, (okdata) => {
if (Array.isArray(okdata)) {
//console.log("fetchSoundChannels : ", okdata)
window.SoundChannelList.push(...okdata);
} else console.log("fetchSoundChannels: okdata is not array");
}, (errdata) => {
alert("Error loading sound channels : " + errdata.message);
});
}
$(document).ready(function () { $(document).ready(function () {
console.log("broadcastzones.js loaded successfully"); console.log("broadcastzones.js loaded successfully");
@@ -67,7 +113,7 @@ $(document).ready(function () {
let $findzone = $('#findzone'); let $findzone = $('#findzone');
$findzone.on('input', function () { $findzone.off('input').on('input', function () {
let searchTerm = $findzone.val().trim().toLowerCase(); let searchTerm = $findzone.val().trim().toLowerCase();
if (searchTerm.length > 0) { if (searchTerm.length > 0) {
window.selectedBroadcastZoneRow = null; window.selectedBroadcastZoneRow = null;
@@ -97,36 +143,35 @@ $(document).ready(function () {
$broadcastzonedescription.val(''); $broadcastzonedescription.val('');
// fill broadcastzonesoundchannel from SoundChannelList // fill broadcastzonesoundchannel from SoundChannelList
$broadcastzonesoundchannel.empty(); $broadcastzonesoundchannel.empty();
if (Array.isArray(SoundChannelList) && SoundChannelList.length > 0) { console.log("SoundChannelList:", window.SoundChannelList);
if (Array.isArray(window.SoundChannelList) && window.SoundChannelList.length > 0) {
// SoundChannelList ada isinya // SoundChannelList ada isinya
SoundChannelList.forEach(ch => { window.SoundChannelList.forEach(ch => {
if (ch.channel && ch.channel.length > 0){ if (ch && ch.length>0){
// hanya yang punya channel saja $broadcastzonesoundchannel.append($('<option>').val(ch).text(ch));
$broadcastzonesoundchannel.append($('<option>').val(ch.channel).text(ch.channel));
} }
}); });
} }
$broadcastzonebox.val(''); $broadcastzonebox.val('1').prop('disabled', true);
for (let i = 1; i <= 32; i++) { for (let i = 1; i <= 32; i++) {
cbRelay(i).prop('checked', false); cbRelay(i).prop('checked', false);
} }
} }
reloadBroadcastZones(APIURL_BroadcastZone, () => { fetchSoundChannels();
fill_broadcastzonetablebody(window.BroadcastZoneList); reloadBroadcastZones(APIURL_BroadcastZone);
});
$btnClear.click(() => { $btnClear.off('click').on('click', () => {
DoClear(APIURL_BroadcastZone, "BroadcastZones", (okdata) => { DoClear(APIURL_BroadcastZone, "BroadcastZones", (okdata) => {
reloadBroadcastZones(APIURL_BroadcastZone); reloadBroadcastZones(APIURL_BroadcastZone);
fill_broadcastzonetablebody(window.BroadcastZoneList);
alert("Success clear broadcast zones: " + okdata.message); alert("Success clear broadcast zones: " + okdata.message);
}, (errdata) => { }, (errdata) => {
alert("Error clear broadcast zones: " + errdata.message); alert("Error clear broadcast zones: " + errdata.message);
}); });
}); });
$btnAdd.click(() => { $btnAdd.off('click').on('click', () => {
$broadcastzonemodal.modal('show'); $broadcastzonemodal.modal('show');
clearBroadcastZoneModal(); clearBroadcastZoneModal();
@@ -165,10 +210,8 @@ $(document).ready(function () {
Relay: relay Relay: relay
}; };
fetchAPI(APIURL_BroadcastZone + "Add", "POST", {}, bz, (okdata) => { fetchAPI(APIURL_BroadcastZone + "Add", "POST", {}, bz, (okdata) => {
reloadBroadcastZones(APIURL_BroadcastZone, () => { reloadBroadcastZones(APIURL_BroadcastZone);
fill_broadcastzonetablebody(window.BroadcastZoneList); alert("Success add new broadcast zone: " + okdata.message);
alert("Success add new broadcast zone: " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error add new broadcast zone: " + errdata.message); alert("Error add new broadcast zone: " + errdata.message);
}); });
@@ -180,12 +223,12 @@ $(document).ready(function () {
}); });
}); });
$btnRemove.click(() => { $btnRemove.off('click').on('click', () => {
if (window.selectedBroadcastZoneRow) { if (window.selectedBroadcastZoneRow) {
let cells = window.selectedBroadcastZoneRow.find('td'); let cells = window.selectedBroadcastZoneRow.find('td');
/** @type {BroadcastZone} */ /** @type {BroadcastZone} */
let bz = { let bz = {
index: cells.eq(0).text(), index: Number(cells.eq(0).text()),
description: cells.eq(1).text(), description: cells.eq(1).text(),
SoundChannel: cells.eq(2).text(), SoundChannel: cells.eq(2).text(),
Box: cells.eq(3).text(), Box: cells.eq(3).text(),
@@ -193,10 +236,8 @@ $(document).ready(function () {
}; };
if (confirm(`Are you sure to delete broadcast zone [${bz.index}] Description=${bz.description}?`)) { if (confirm(`Are you sure to delete broadcast zone [${bz.index}] Description=${bz.description}?`)) {
fetchAPI(APIURL_BroadcastZone + "DeleteByIndex/" + bz.index, "DELETE", {}, null, (okdata) => { fetchAPI(APIURL_BroadcastZone + "DeleteByIndex/" + bz.index, "DELETE", {}, null, (okdata) => {
reloadBroadcastZones(APIURL_BroadcastZone, () => { reloadBroadcastZones(APIURL_BroadcastZone);
fill_broadcastzonetablebody(window.BroadcastZoneList); alert("Success delete broadcast zone: " + okdata.message);
alert("Success delete broadcast zone: " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error delete broadcast zone: " + errdata.message); alert("Error delete broadcast zone: " + errdata.message);
}); });
@@ -204,12 +245,12 @@ $(document).ready(function () {
} }
}); });
$btnEdit.click(() => { $btnEdit.off('click').on('click', () => {
if (window.selectedBroadcastZoneRow) { if (window.selectedBroadcastZoneRow) {
let cells = window.selectedBroadcastZoneRow.find('td'); let cells = window.selectedBroadcastZoneRow.find('td');
/** @type {BroadcastZone} */ /** @type {BroadcastZone} */
let bz = { let bz = {
index: cells.eq(0).text(), index: Number(cells.eq(0).text()),
description: cells.eq(1).text(), description: cells.eq(1).text(),
SoundChannel: cells.eq(2).text(), SoundChannel: cells.eq(2).text(),
Box: cells.eq(3).text(), Box: cells.eq(3).text(),
@@ -221,12 +262,12 @@ $(document).ready(function () {
$broadcastzoneindex.val(bz.index); $broadcastzoneindex.val(bz.index);
$broadcastzonedescription.val(bz.description); $broadcastzonedescription.val(bz.description);
$broadcastzonesoundchannel.val(bz.SoundChannel); $broadcastzonesoundchannel.val(bz.SoundChannel);
$broadcastzonebox.val(bz.id); $broadcastzonebox.val(bz.Box);
if (bz.bp) { if (bz.Relay && bz.Relay.length > 0) {
bz.bp.split(';').forEach(relayId => { bz.Relay.split(';').map(Number).filter(n => !isNaN(n) && n>=1 && n<=8).forEach(relayId => {
let id = parseInt(relayId, 10); cbRelay(relayId).prop('checked', true);
cbRelay(id).prop('checked', true);
}); });
} }
$broadcastzonemodal.off('click.broadcastzonesave').on('click.broadcastzonesave', '#broadcastzonesave', function () { $broadcastzonemodal.off('click.broadcastzonesave').on('click.broadcastzonesave', '#broadcastzonesave', function () {
let description = $broadcastzonedescription.val().trim(); let description = $broadcastzonedescription.val().trim();
@@ -262,10 +303,8 @@ $(document).ready(function () {
Relay: relay Relay: relay
}; };
fetchAPI(APIURL_BroadcastZone + "UpdateByIndex/" + bz.index, "PATCH", {}, bzUpdate, (okdata) => { fetchAPI(APIURL_BroadcastZone + "UpdateByIndex/" + bz.index, "PATCH", {}, bzUpdate, (okdata) => {
reloadBroadcastZones(APIURL_BroadcastZone, () => { reloadBroadcastZones(APIURL_BroadcastZone);
fill_broadcastzonetablebody(window.BroadcastZoneList); alert("Success edit broadcast zone: " + okdata.message);
alert("Success edit broadcast zone: " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error edit broadcast zone: " + errdata.message); alert("Error edit broadcast zone: " + errdata.message);
}); });
@@ -278,16 +317,14 @@ $(document).ready(function () {
} }
}); });
$btnExport.click(() => { $btnExport.off('click').on('click', () => {
DoExport(APIURL_BroadcastZone, "broadcastzones.xlsx", {}); DoExport(APIURL_BroadcastZone, "broadcastzones.xlsx", {});
}); });
$btnImport.click(() => { $btnImport.off('click').on('click', () => {
DoImport(APIURL_BroadcastZone, (okdata) => { DoImport(APIURL_BroadcastZone, (okdata) => {
reloadBroadcastZones(APIURL_BroadcastZone, () => { reloadBroadcastZones(APIURL_BroadcastZone);
fill_broadcastzonetablebody(window.BroadcastZoneList); alert("Success import broadcast zones: " + okdata.message);
alert("Success import broadcast zones: " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error importing broadcast zones from XLSX: " + errdata.message); alert("Error importing broadcast zones from XLSX: " + errdata.message);
}); });

View File

@@ -0,0 +1,7 @@
document.addEventListener('DOMContentLoaded', function() {
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bss-tooltip]'));
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
})
}, false);

View File

@@ -1,4 +1,15 @@
/**
* @typedef {Object} LanguageBank
* @property {number} index
* @property {string} tag
* @property {string} language
*
*/
/** List of Languagebank data loaded from server
* @type {LanguageBank[]}
*/
window.languagebankdata = [];
/** /**
* Currently selected languagebank row in the table * Currently selected languagebank row in the table
* @type {JQuery<HTMLElement>|null} * @type {JQuery<HTMLElement>|null}
@@ -39,7 +50,22 @@ function fill_languagebanktablebody(vv) {
$('#tablesize').text("Table Size: " + vv.length); $('#tablesize').text("Table Size: " + vv.length);
} }
/**
* Reload language bank from server
* @param {string} APIURL API URL endpoint, default "LanguageLink/"
*/
function reloadLanguageBank(APIURL = "LanguageLink/") {
window.languagebankdata = [];
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
if (Array.isArray(okdata)) {
window.languagebankdata.push(...okdata);
window.selectedlanguagerow = null;
fill_languagebanktablebody(window.languagebankdata);
}
}, (errdata) => {
alert("Error loading languagebank : " + errdata.message);
});
}
$(document).ready(function () { $(document).ready(function () {
console.log('languagebank.js loaded'); console.log('languagebank.js loaded');
@@ -89,15 +115,11 @@ $(document).ready(function () {
} }
}); });
reloadLanguageBank(APIURL, () => { reloadLanguageBank(APIURL);
fill_languagebanktablebody(window.languagebankdata);
});
$btnClear.click(() => { $btnClear.click(() => {
DoClear(APIURL, "LanguageLink", (okdata) => { DoClear(APIURL, "LanguageLink", (okdata) => {
reloadLanguageBank(APIURL, () => { reloadLanguageBank(APIURL);
fill_languagebanktablebody(window.languagebankdata); alert("Success clear languageLink : " + okdata.message);
alert("Success clear languageLink : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error clear languageLink : " + errdata.message); alert("Error clear languageLink : " + errdata.message);
}); });
@@ -134,10 +156,8 @@ $(document).ready(function () {
language: langString language: langString
} }
fetchAPI(APIURL + "Add", "POST", {}, ll, (okdata) => { fetchAPI(APIURL + "Add", "POST", {}, ll, (okdata) => {
reloadLanguageBank(APIURL, () => { alert("Success add language : " + okdata.message);
fill_languagebanktablebody(window.languagebankdata); reloadLanguageBank(APIURL);
alert("Success add language : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error add language : " + errdata.message); alert("Error add language : " + errdata.message);
}); });
@@ -155,16 +175,14 @@ $(document).ready(function () {
let cells = window.selectedlanguagerow.find('td'); let cells = window.selectedlanguagerow.find('td');
/** @type {Language} */ /** @type {Language} */
let ll = { let ll = {
index: cells.eq(0).text(), index: Number(cells.eq(0).text()),
tag: cells.eq(1).text(), tag: cells.eq(1).text(),
language: cells.eq(2).text() language: cells.eq(2).text()
} }
if (confirm(`Are you sure to delete language [${ll.index}] Tag=${ll.tag} Language=${ll.language}?`)) { if (confirm(`Are you sure to delete language [${ll.index}] Tag=${ll.tag} Language=${ll.language}?`)) {
fetchAPI(APIURL + "DeleteByIndex/" + ll.index, "DELETE", {}, null, (okdata) => { fetchAPI(APIURL + "DeleteByIndex/" + ll.index, "DELETE", {}, null, (okdata) => {
reloadLanguageBank(APIURL, () => { reloadLanguageBank(APIURL);
fill_languagebanktablebody(window.languagebankdata); alert("Success delete language : " + okdata.message);
alert("Success delete language : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error delete language : " + errdata.message); alert("Error delete language : " + errdata.message);
}); });
@@ -176,7 +194,7 @@ $(document).ready(function () {
let cells = window.selectedlanguagerow.find('td'); let cells = window.selectedlanguagerow.find('td');
/** @type {Language} */ /** @type {Language} */
let ll = { let ll = {
index: cells.eq(0).text(), index: Number(cells.eq(0).text()),
tag: cells.eq(1).text(), tag: cells.eq(1).text(),
language: cells.eq(2).text() language: cells.eq(2).text()
} }
@@ -221,10 +239,8 @@ $(document).ready(function () {
ll.tag = tag; ll.tag = tag;
ll.language = langString; ll.language = langString;
fetchAPI(APIURL + "UpdateByIndex/" + ll.index, "PATCH", {}, ll, (okdata) => { fetchAPI(APIURL + "UpdateByIndex/" + ll.index, "PATCH", {}, ll, (okdata) => {
reloadLanguageBank(APIURL, () => { reloadLanguageBank(APIURL);
fill_languagebanktablebody(window.languagebankdata); alert("Success edit language : " + okdata.message);
alert("Success edit language : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error edit language : " + errdata.message); alert("Error edit language : " + errdata.message);
}); });
@@ -246,10 +262,8 @@ $(document).ready(function () {
}); });
$btnImport.click(() => { $btnImport.click(() => {
DoImport(APIURL, (okdata) => { DoImport(APIURL, (okdata) => {
reloadLanguageBank(APIURL, () => { reloadLanguageBank(APIURL);
fill_languagebanktablebody(window.languagebankdata); alert("Success import languagebank : " + okdata.message);
alert("Success import languagebank : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error importing languagebank from XLSX : " + errdata.message); alert("Error importing languagebank from XLSX : " + errdata.message);
}); });

View File

@@ -1,4 +1,19 @@
/**
* @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[]}
*/
window.messagebankdata ??= [];
/** /**
* Currently selected messagebank row in the table * Currently selected messagebank row in the table
* @type {JQuery<HTMLElement>|null} * @type {JQuery<HTMLElement>|null}
@@ -44,7 +59,22 @@ function fill_messagebanktablebody(vv) {
$('#tablesize').text("Table Size: " + vv.length); $('#tablesize').text("Table Size: " + vv.length);
} }
/**
* Reload message bank from server
* @param {string} APIURL API URL endpoint, default "MessageBank/"
*/
function reloadMessageBank(APIURL = "MessageBank/") {
window.messagebankdata ??= [];
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
if (Array.isArray(okdata)) {
window.messagebankdata.push(...okdata);
window.selectedmessagerow = null;
fill_messagebanktablebody(window.messagebankdata);
}
}, (errdata) => {
alert("Error loading messagebank : " + errdata.message);
});
}
$(document).ready(function () { $(document).ready(function () {
console.log("messagebank.js loaded"); console.log("messagebank.js loaded");
@@ -178,15 +208,11 @@ $(document).ready(function () {
}); });
reloadMessageBank(APIURL, () => { reloadMessageBank(APIURL);
fill_messagebanktablebody(window.messagebankdata);
});
$btnClear.click(() => { $btnClear.click(() => {
DoClear(APIURL, "Messagebank", (okdata) => { DoClear(APIURL, "Messagebank", (okdata) => {
reloadMessageBank(APIURL, () => { reloadMessageBank(APIURL);
fill_messagebanktablebody(window.messagebankdata); alert("Success clear messagebank : " + okdata.message);
alert("Success clear messagebank : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error clear messagebank : " + errdata.message); alert("Error clear messagebank : " + errdata.message);
}); });
@@ -261,10 +287,8 @@ $(document).ready(function () {
}; };
// send to server using fetchAPI // send to server using fetchAPI
fetchAPI(APIURL + "Add", "POST", mb, null, (okdata) => { fetchAPI(APIURL + "Add", "POST", mb, null, (okdata) => {
reloadMessageBank(APIURL, () => { reloadMessageBank(APIURL);
fill_messagebanktablebody(window.messagebankdata); alert("Success add new messagebank : " + okdata.message);
alert("Success add new messagebank : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error add new messagebank : " + errdata.message); alert("Error add new messagebank : " + errdata.message);
}); });
@@ -281,7 +305,7 @@ $(document).ready(function () {
let cells = window.selectedmessagerow.find('td'); let cells = window.selectedmessagerow.find('td');
/** @type {MessageBank} */ /** @type {MessageBank} */
let mb = { let mb = {
index: cells.eq(0).text(), index: Number(cells.eq(0).text()),
description: cells.eq(1).text(), description: cells.eq(1).text(),
language: cells.eq(2).text(), language: cells.eq(2).text(),
aNN_ID: parseInt(cells.eq(3).text()), aNN_ID: parseInt(cells.eq(3).text()),
@@ -292,10 +316,8 @@ $(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} `)) { 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) => { fetchAPI(APIURL + "DeleteByIndex/" + mb.index, "DELETE", {}, null, (okdata) => {
reloadMessageBank(APIURL, () => { reloadMessageBank(APIURL);
fill_messagebanktablebody(window.messagebankdata); alert("Success delete messagebank : " + okdata.message);
alert("Success delete messagebank : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error delete messagebank : " + errdata.message); alert("Error delete messagebank : " + errdata.message);
}); });
@@ -307,7 +329,7 @@ $(document).ready(function () {
let cells = window.selectedmessagerow.find('td'); let cells = window.selectedmessagerow.find('td');
/** @type {MessageBank} */ /** @type {MessageBank} */
let mb = { let mb = {
index: cells.eq(0).text(), index: Number(cells.eq(0).text()),
description: cells.eq(1).text(), description: cells.eq(1).text(),
language: cells.eq(2).text(), language: cells.eq(2).text(),
aNN_ID: parseInt(cells.eq(3).text()), aNN_ID: parseInt(cells.eq(3).text()),
@@ -401,10 +423,8 @@ $(document).ready(function () {
Message_TAGS: messagetags Message_TAGS: messagetags
}; };
fetchAPI(APIURL + "UpdateByIndex/" + mb.index, "PATCH", mbUpdate, null, (okdata) => { fetchAPI(APIURL + "UpdateByIndex/" + mb.index, "PATCH", mbUpdate, null, (okdata) => {
reloadMessageBank(APIURL, () => { reloadMessageBank(APIURL);
fill_messagebanktablebody(window.messagebankdata); alert("Success edit messagebank : " + okdata.message);
alert("Success edit messagebank : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error edit messagebank : " + errdata.message); alert("Error edit messagebank : " + errdata.message);
}); });
@@ -423,11 +443,8 @@ $(document).ready(function () {
}); });
$btnImport.click(() => { $btnImport.click(() => {
DoImport(APIURL, (okdata) => { DoImport(APIURL, (okdata) => {
reloadMessageBank(APIURL, () => { reloadMessageBank(APIURL);
fill_messagebanktablebody(window.messagebankdata); alert("Success import messagebank : " + okdata.message);
alert("Success import messagebank : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error importing messagebank from XLSX : " + errdata.message); alert("Error importing messagebank from XLSX : " + errdata.message);
}); });

View File

@@ -1,4 +1,20 @@
/**
* @typedef {Object} ScheduleBank
* @property {number} index
* @property {string} description
* @property {string} day
* @property {string} time
* @property {string} soundpath
* @property {number} repeat
* @property {boolean} enable
* @property {string} broadcastZones
* @property {string} language
*/
/** List of Schedulebank data loaded from server
* @type {ScheduleBank[]}
*/
window.schedulebankdata = [];
/** /**
* Currently selected schedulebank row in the table * Currently selected schedulebank row in the table
* @type {JQuery<HTMLElement>|null} * @type {JQuery<HTMLElement>|null}
@@ -45,7 +61,22 @@ function fill_schedulebanktablebody(vv) {
$('#tablesize').text("Table Size: " + vv.length); $('#tablesize').text("Table Size: " + vv.length);
} }
/**
* Reload timer bank from server
* @param {string} APIURL API URL endpoint, default "ScheduleBank/"
*/
function reloadTimerBank(APIURL = "ScheduleBank/") {
window.schedulebankdata = [];
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
if (Array.isArray(okdata)) {
window.schedulebankdata.push(...okdata);
selectedschedulerow = null;
fill_schedulebanktablebody(window.schedulebankdata);
}
}, (errdata) => {
alert("Error loading schedulebank : " + errdata.message);
});
}
$(document).ready(function () { $(document).ready(function () {
console.log("schedulebank.js loaded successfully"); console.log("schedulebank.js loaded successfully");
@@ -70,7 +101,9 @@ $(document).ready(function () {
let $schedulehour = $schedulemodal.find('#schedulehour'); let $schedulehour = $schedulemodal.find('#schedulehour');
// number input 0-59 // number input 0-59
let $scheduleminute = $schedulemodal.find('#scheduleminute'); let $scheduleminute = $schedulemodal.find('#scheduleminute');
// select for messagebank
//TODO ganti dropdown with list of messages from MessageBank
// text input
let $schedulesoundpath = $schedulemodal.find('#schedulesoundpath'); let $schedulesoundpath = $schedulemodal.find('#schedulesoundpath');
// number input 0-5 // number input 0-5
let $schedulerepeat = $schedulemodal.find('#schedulerepeat'); let $schedulerepeat = $schedulemodal.find('#schedulerepeat');
@@ -106,17 +139,8 @@ $(document).ready(function () {
$scheduledescription.val(''); $scheduledescription.val('');
$schedulehour.val('0'); $schedulehour.val('0');
$scheduleminute.val('0'); $scheduleminute.val('0');
$schedulesoundpath.val('');
$schedulesoundpath.empty(); $schedulerepeat.val('0');
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); $scheduleenable.prop('checked', true);
$scheduleeveryday.prop('checked', false); $scheduleeveryday.prop('checked', false);
$schedulesunday.prop('checked', false); $schedulesunday.prop('checked', false);
@@ -149,15 +173,11 @@ $(document).ready(function () {
}); });
reloadTimerBank(APIURL, () => { reloadTimerBank(APIURL);
fill_schedulebanktablebody(window.schedulebankdata);
});
$btnClear.click(() => { $btnClear.click(() => {
DoClear(APIURL, "Timerbank", (okdata) => { DoClear(APIURL, "Timerbank", (okdata) => {
reloadTimerBank(APIURL,() => { reloadTimerBank(APIURL);
fill_schedulebanktablebody(window.schedulebankdata); alert("Success clear schedulebank : " + okdata.message);
alert("Success clear schedulebank : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error clear schedulebank : " + errdata.message); alert("Error clear schedulebank : " + errdata.message);
}); });
@@ -218,10 +238,8 @@ $(document).ready(function () {
}; };
fetchAPI(APIURL + "Add", "POST", {}, scheduleObj, (okdata) => { fetchAPI(APIURL + "Add", "POST", {}, scheduleObj, (okdata) => {
reloadTimerBank(APIURL, () => { alert("Success add schedule: " + okdata.message);
fill_schedulebanktablebody(window.schedulebankdata); reloadTimerBank(APIURL);
alert("Success add schedule: " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error add schedule: " + errdata.message); alert("Error add schedule: " + errdata.message);
}); });
@@ -234,7 +252,7 @@ $(document).ready(function () {
let cells = window.selectedschedulerow.find('td'); let cells = window.selectedschedulerow.find('td');
/** @type {ScheduleBank} */ /** @type {ScheduleBank} */
let sr = { let sr = {
index: cells.eq(0).text(), index: Number(cells.eq(0).text()),
description: cells.eq(1).text(), description: cells.eq(1).text(),
day: cells.eq(2).text(), day: cells.eq(2).text(),
time: cells.eq(3).text(), time: cells.eq(3).text(),
@@ -246,10 +264,8 @@ $(document).ready(function () {
} }
if (confirm(`Are you sure to delete schedule [${sr.index}] Description=${sr.description}?`)) { if (confirm(`Are you sure to delete schedule [${sr.index}] Description=${sr.description}?`)) {
fetchAPI(APIURL + "DeleteByIndex/" + sr.index, "DELETE", {}, null, (okdata) => { fetchAPI(APIURL + "DeleteByIndex/" + sr.index, "DELETE", {}, null, (okdata) => {
reloadTimerBank(APIURL, () => { reloadTimerBank(APIURL);
fill_schedulebanktablebody(window.schedulebankdata); alert("Success delete schedule : " + okdata.message);
alert("Success delete schedule : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error delete schedule : " + errdata.message); alert("Error delete schedule : " + errdata.message);
}); });
@@ -261,7 +277,7 @@ $(document).ready(function () {
let cells = window.selectedschedulerow.find('td'); let cells = window.selectedschedulerow.find('td');
/** @type {ScheduleBank} */ /** @type {ScheduleBank} */
let sr = { let sr = {
index: cells.eq(0).text(), index: Number(cells.eq(0).text()),
description: cells.eq(1).text(), description: cells.eq(1).text(),
day: cells.eq(2).text(), day: cells.eq(2).text(),
time: cells.eq(3).text(), time: cells.eq(3).text(),
@@ -366,10 +382,8 @@ $(document).ready(function () {
}; };
fetchAPI(APIURL + "UpdateByIndex/" + sr.index, "PATCH", {}, scheduleObj, (okdata) => { fetchAPI(APIURL + "UpdateByIndex/" + sr.index, "PATCH", {}, scheduleObj, (okdata) => {
reloadTimerBank(APIURL, () => { alert("Success edit schedule: " + okdata.message);
fill_schedulebanktablebody(window.schedulebankdata); reloadTimerBank(APIURL);
alert("Success edit schedule: " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error edit schedule: " + errdata.message); alert("Error edit schedule: " + errdata.message);
}); });
@@ -384,10 +398,8 @@ $(document).ready(function () {
}); });
$btnImport.click(() => { $btnImport.click(() => {
DoImport(APIURL, (okdata) => { DoImport(APIURL, (okdata) => {
reloadTimerBank(APIURL, () => { reloadTimerBank(APIURL);
fill_schedulebanktablebody(window.schedulebankdata); alert("Success import schedulebank from XLSX : " + okdata.message);
alert("Success import schedulebank from XLSX : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error importing schedulebank from XLSX : " + errdata.message); alert("Error importing schedulebank from XLSX : " + errdata.message);
}); });

View File

@@ -20,263 +20,6 @@ window.languages = [];
*/ */
window.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 * Create a list item element
* @param {String} text Text Content for the list item * @param {String} text Text Content for the list item
@@ -568,13 +311,7 @@ $(document).ready(function () {
getCategories(); getCategories();
getLanguages(); getLanguages();
getScheduledDays(); getScheduledDays();
reloadMessageBank();
reloadTimerBank();
reloadBroadcastZones();
reloadSoundBank();
reloadSoundbankFiles();
reloadSoundChannel();
reloadLanguageBank();
// Initialize WebSocket connection // Initialize WebSocket connection
window.ws = new WebSocket(wsURL); window.ws = new WebSocket(wsURL);

2
html/webpage/assets/js/select2.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,25 @@
/**
* @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[]}
*/
window.soundbankdata = [];
/** /**
* Currently selected soundbank row in the table * Currently selected soundbank row in the table
* @type {JQuery<HTMLElement>|null} * @type {JQuery<HTMLElement>|null}
@@ -7,13 +28,37 @@ window.selectedsoundrow = null;
/**
* 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/"
*/
function reloadSoundBank(APIURL = "SoundBank/") {
window.soundbankdata = [];
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
if (Array.isArray(okdata)) {
window.soundbankdata.push(...okdata);
window.selectedsoundrow = null;
fill_soundbanktablebody(window.soundbankdata);
}
}, (errdata) => {
alert("Error loading soundbank : " + errdata.message);
});
}
/** /**
* Fill soundbank table body with values * Fill soundbank table body with values
* @param {SoundBank[]} vv values to fill * @param {SoundBank[]} vv values to fill
*/ */
function fill_soundbanktablebody(vv) { function fill_soundbanktablebody(vv) {
$('#soundbanktablebody').empty(); $('#soundbanktablebody').empty();
console.log("Filling soundbank table with " + vv.length + " items");
if (!Array.isArray(vv) || vv.length === 0) return; if (!Array.isArray(vv) || vv.length === 0) return;
vv.forEach(item => { vv.forEach(item => {
const row = `<tr> const row = `<tr>
@@ -48,12 +93,60 @@ function fill_soundbanktablebody(vv) {
$('#tablesize').text("Table Size: " + vv.length); $('#tablesize').text("Table Size: " + vv.length);
} }
/**
* Reload soundbank files from server and filter by language, category, and voiceType
* @param {String} language
* @param {String} category
* @param {String} voiceType
* @param {Function} cb callback function when done
*/
function reloadSoundbankFiles(language, category, voiceType, cb=null) {
window.select2data = [];
$('#modalpath').empty().trigger('change');
if (language && language.length > 0) {
if (category && category.length > 0) {
if (voiceType && voiceType.length > 0) {
fetchAPI(`ListFiles/${language}/${voiceType}/${category}`, "GET", {}, null, (okdata) => {
console.log("reloadSoundbankFiles: got " + okdata.length + " items");
if (Array.isArray(okdata)){
window.select2data = okdata.map(p => ({id: getFilenameFromPath(p), text: getFilenameFromPath(p)}));
$('#modalpath').select2({
data: window.select2data,
placeholder: 'Select a sound file',
allowClear: true,
width: '100%',
dropdownParent: $('#soundbankmodal')
});
if (cb) cb();
}
}, (errdata) => {
alert("Error loading soundbank files : " + errdata.message);
});
}
}
}
}
function getFilenameFromPath(path) {
if (!path || path.length === 0) return "";
if (path.includes('\\')) {
let parts = path.split('\\');
return parts[parts.length - 1];
} else if (!path.includes('/')) {
let parts = path.split('/');
return parts[parts.length - 1];
}
}
$(document).ready(function () { $(document).ready(function () {
console.log("soundbank.js loaded successfully"); console.log("soundbank.js loaded successfully");
reloadSoundbankFiles();
$('#soundbanktablebody').empty(); $('#soundbanktablebody').empty();
window.selectedsoundrow = null; window.selectedsoundrow = null;
let $btnClear = $('#btnClear'); let $btnClear = $('#btnClear');
@@ -72,7 +165,9 @@ $(document).ready(function () {
let $modalcategory = $modal.find('#modalcategory'); let $modalcategory = $modal.find('#modalcategory');
let $modallanguage = $modal.find('#modallanguage'); let $modallanguage = $modal.find('#modallanguage');
let $modalvoicetype = $modal.find('#modalvoicetype'); let $modalvoicetype = $modal.find('#modalvoicetype');
let $modalpath = $modal.find('#modalpath'); let selected_category = null;
let selected_language = null;
let selected_voicetype = null;
/** /**
* Clear soundbank modal inputs * Clear soundbank modal inputs
@@ -99,18 +194,11 @@ $(document).ready(function () {
$modalvoicetype.append(new Option(vt, vt)); $modalvoicetype.append(new Option(vt, vt));
}); });
$modalvoicetype.val(null); $modalvoicetype.val(null);
// fill modalpath options from soundbankfiles[] $('#modalpath').select2()
// TODO read https://jeesite.com/front/jquery-select2/4.0/index.htm
console.log("window.select2data has " + window.select2data.length + " items");
$('#modalpath').select2({
data: window.select2data
})
} }
reloadSoundBank(APIURL, () => { reloadSoundBank(APIURL);
fill_soundbanktablebody(window.soundbankdata);
});
$('#findsoundbank').on('input', function () { $('#findsoundbank').on('input', function () {
let searchTerm = $(this).val().trim().toLowerCase(); let searchTerm = $(this).val().trim().toLowerCase();
if (searchTerm.length > 0) { if (searchTerm.length > 0) {
@@ -124,23 +212,88 @@ $(document).ready(function () {
}); });
$btnClear.click(() => { $btnClear.click(() => {
DoClear(APIURL, "Soundbank", (okdata) => { DoClear(APIURL, "Soundbank", (okdata) => {
reloadSoundBank(APIURL, () => { reloadSoundBank(APIURL);
fill_soundbanktablebody(window.soundbankdata); alert("Success clear soundbank : " + okdata.message);
alert("Success clear soundbank : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error clear soundbank : " + errdata.message); alert("Error clear soundbank : " + errdata.message);
}); });
}); });
function SetupEventForCategoryLanguageVoiceType() {
$modalcategory.off('change').on('change', function () {
selected_category = $(this).val();
reloadSoundbankFiles(selected_language, selected_category, selected_voicetype);
});
$modallanguage.off('change').on('change', function () {
selected_language = $(this).val();
reloadSoundbankFiles(selected_language, selected_category, selected_voicetype);
});
$modalvoicetype.off('change').on('change', function () {
selected_voicetype = $(this).val();
reloadSoundbankFiles(selected_language, selected_category, selected_voicetype);
});
}
$btnAdd.click(() => { $btnAdd.click(() => {
$modal.modal('show'); $modal.modal('show');
clearSoundbankModal(); clearSoundbankModal();
// event on selection change of language, category, voiceType
SetupEventForCategoryLanguageVoiceType();
// event on Click save button // event on Click save button
$modal.off('click.soundbanksave').on('click.soundbanksave', '#soundbanksave', function () { $modal.off('click.soundbanksave').on('click.soundbanksave', '#soundbanksave', function () {
//TODO Add soundbank save process here let description = $modaldescription.val().trim();
let tag = $modaltag.val().trim();
let category = $modalcategory.val();
let language = $modallanguage.val();
let voiceType = $modalvoicetype.val();
let path = $('#soundbankmodal #modalpath').val();
if (!description || description.length === 0) {
alert("Description is required");
return;
}
if (!tag || tag.length === 0) {
alert("Tag is required");
return;
}
if (!category || category.length === 0) {
alert("Category is required");
return;
}
if (!language || language.length === 0) {
alert("Language is required");
return;
}
if (!voiceType || voiceType.length === 0) {
alert("Voice Type is required");
return;
}
if (!path || path.length === 0) {
alert("Path is required");
return;
}
$modal.modal('hide'); $modal.modal('hide');
/**
* @type {SoundBank}
*/
let nsb = {
index: 0,
Description: description,
TAG: tag,
Category: category,
Language: language,
VoiceType: voiceType,
Path: path
}
fetchAPI(APIURL + "Add", "POST", {}, nsb, (okdata) => {
reloadSoundBank(APIURL);
alert("Success add soundbank : " + okdata.message);
}, (errdata) => {
alert("Error add soundbank : " + errdata.message);
});
}); });
// event on Click close button // event on Click close button
$modal.off('click.soundbankclose').on('click.soundbankclose', '#soundbankclose', function () { $modal.off('click.soundbankclose').on('click.soundbankclose', '#soundbankclose', function () {
@@ -152,7 +305,7 @@ $(document).ready(function () {
let cells = window.selectedsoundrow.find('td'); let cells = window.selectedsoundrow.find('td');
/** @type {SoundBank} */ /** @type {SoundBank} */
let sb = { let sb = {
index: cells.eq(0).text(), index: Number(cells.eq(0).text()),
description: cells.eq(1).text(), description: cells.eq(1).text(),
tag: cells.eq(2).text(), tag: cells.eq(2).text(),
category: cells.eq(3).text(), category: cells.eq(3).text(),
@@ -162,10 +315,8 @@ $(document).ready(function () {
} }
if (confirm(`Are you sure to delete soundbank [${sb.index}] Description=${sb.description} Tag=${sb.tag}?`)) { if (confirm(`Are you sure to delete soundbank [${sb.index}] Description=${sb.description} Tag=${sb.tag}?`)) {
fetchAPI(APIURL + "DeleteByIndex/" + sb.index, "DELETE", {}, null, (okdata) => { fetchAPI(APIURL + "DeleteByIndex/" + sb.index, "DELETE", {}, null, (okdata) => {
reloadSoundBank(APIURL, () => { reloadSoundBank(APIURL);
fill_soundbanktablebody(window.soundbankdata); alert("Success delete soundbank : " + okdata.message);
alert("Success delete soundbank : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error delete soundbank : " + errdata.message); alert("Error delete soundbank : " + errdata.message);
}); });
@@ -177,28 +328,91 @@ $(document).ready(function () {
let cells = window.selectedsoundrow.find('td'); let cells = window.selectedsoundrow.find('td');
/** @type {SoundBank} */ /** @type {SoundBank} */
let sb = { let sb = {
index: cells.eq(0).text(), index: Number(cells.eq(0).text()),
description: cells.eq(1).text(), Description: cells.eq(1).text(),
tag: cells.eq(2).text(), TAG: cells.eq(2).text(),
category: cells.eq(3).text(), Category: cells.eq(3).text(),
language: cells.eq(4).text(), Language: cells.eq(4).text(),
voiceType: cells.eq(5).text(), VoiceType: cells.eq(5).text(),
path: cells.eq(6).text() Path: cells.eq(6).text()
} }
if (confirm(`Are you sure to edit soundbank [${sb.index}] Description=${sb.description} Tag=${sb.tag}?`)) { if (confirm(`Are you sure to edit soundbank [${sb.index}] Description=${sb.Description} Tag=${sb.TAG}?`)) {
$modal.modal('show'); $modal.modal('show');
$modal.off('hidden.bs.modal').on('hidden.bs.modal', function () { clearSoundbankModal();
SetupEventForCategoryLanguageVoiceType();
$modalindex.val(sb.index).prop('disabled', true);
$modaldescription.val(sb.Description);
$modaltag.val(sb.TAG);
$modalcategory.val(sb.Category);
selected_category = sb.Category;
$modallanguage.val(sb.Language);
selected_language = sb.Language;
$modalvoicetype.val(sb.VoiceType);
selected_voicetype = sb.VoiceType;
// load soundbank files for selected language, category, voiceType
reloadSoundbankFiles(selected_language, selected_category, selected_voicetype, () => {
// set selected path
$('#modalpath').val(getFilenameFromPath(sb.Path)).trigger('change');
});
// event on Click save button
// event on Click save button
$modal.off('click.soundbanksave').on('click.soundbanksave', '#soundbanksave', function () { $modal.off('click.soundbanksave').on('click.soundbanksave', '#soundbanksave', function () {
//TODO Add soundbank save process here let description = $modaldescription.val().trim();
let tag = $modaltag.val().trim();
let category = $modalcategory.val();
let language = $modallanguage.val();
let voiceType = $modalvoicetype.val();
let path = $('#soundbankmodal #modalpath').val();
if (!description || description.length === 0) {
alert("Description is required");
return;
}
if (!tag || tag.length === 0) {
alert("Tag is required");
return;
}
if (!category || category.length === 0) {
alert("Category is required");
return;
}
if (!language || language.length === 0) {
alert("Language is required");
return;
}
if (!voiceType || voiceType.length === 0) {
alert("Voice Type is required");
return;
}
if (!path || path.length === 0) {
alert("Path is required");
return;
}
if (description === sb.Description && tag === sb.TAG && category === sb.Category && language === sb.Language && voiceType === sb.VoiceType && path === sb.Path) {
alert("No changes detected");
return;
}
sb.Description = description;
sb.TAG = tag;
sb.Category = category;
sb.Language = language;
sb.VoiceType = voiceType;
sb.Path = path;
fetchAPI(APIURL + "UpdateByIndex/" + sb.index, "PATCH", {}, sb, (okdata) => {
reloadSoundBank(APIURL);
alert("Success update soundbank : " + okdata.message);
}, (errdata) => {
alert("Error update soundbank : " + errdata.message);
});
$modal.modal('hide'); $modal.modal('hide');
}); });
// event on Click close button // event on Click close button
$modal.off('click.soundbankclose').on('click.soundbankclose', '#soundbankclose', function () { $modal.off('click.soundbankclose').on('click.soundbankclose', '#soundbankclose', function () {
$modal.modal('hide'); $modal.modal('hide');
}); });
});
} }
} }
}); });
@@ -207,10 +421,8 @@ $(document).ready(function () {
}); });
$btnImport.click(() => { $btnImport.click(() => {
DoImport(APIURL, (okdata) => { DoImport(APIURL, (okdata) => {
reloadSoundBank(APIURL, () => { reloadSoundBank(APIURL);
fill_soundbanktablebody(window.soundbankdata); alert("Success import soundbank : " + okdata.message);
alert("Success import soundbank : " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error importing soundbank from XLSX : " + errdata.message); alert("Error importing soundbank from XLSX : " + errdata.message);
}); });

View File

@@ -1,4 +1,14 @@
/**
* @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 = [];
// Currently selected sound channel row in the table // Currently selected sound channel row in the table
window.selectedSoundChannel = null; window.selectedSoundChannel = null;
@@ -8,10 +18,9 @@ window.selectedSoundChannel = null;
* @param {SoundChannel[]} vv Sound channel data to populate the table. * @param {SoundChannel[]} vv Sound channel data to populate the table.
*/ */
function fill_soundchanneltablebody(vv) { function fill_soundchanneltablebody(vv) {
let $tbody = $('#soundchanneltablebody');
let $btnEditSoundChannel = $('#btnEditSoundChannel'); let $btnEditSoundChannel = $('#btnEditSoundChannel');
let $tablesizeSoundChannel = $('#tablesizeSoundChannel'); let $tablesizeSoundChannel = $('#tablesizeSoundChannel');
$tbody.empty(); $('#soundchanneltablebody').empty();
$tablesizeSoundChannel.text('Table Length : N/A'); $tablesizeSoundChannel.text('Table Length : N/A');
if (!Array.isArray(vv) || vv.length === 0) return; if (!Array.isArray(vv) || vv.length === 0) return;
@@ -22,9 +31,9 @@ function fill_soundchanneltablebody(vv) {
<td>${item.channel}</td> <td>${item.channel}</td>
<td>${item.ip}</td> <td>${item.ip}</td>
</tr>`; </tr>`;
$tbody.append(row); $('#soundchanneltablebody').append(row);
let $addedrow = $('#soundchanneltablebody tr:last'); let $addedrow = $('#soundchanneltablebody tr:last');
$addedrow.click(function () { $addedrow.off('click').on('click', function () {
if (selectedSoundChannel) { if (selectedSoundChannel) {
selectedSoundChannel.find('td').css('background-color', ''); selectedSoundChannel.find('td').css('background-color', '');
if (selectedSoundChannel.is($(this))) { if (selectedSoundChannel.is($(this))) {
@@ -41,7 +50,22 @@ function fill_soundchanneltablebody(vv) {
$tablesizeSoundChannel.text("Table Size: " + vv.length); $tablesizeSoundChannel.text("Table Size: " + vv.length);
} }
/**
* Reload sound channels from server
* @param {String} APIURL API URL endpoint (default "SoundChannel/")
*/
function reloadSoundChannel(APIURL = "SoundChannel/") {
window.soundChannels = [];
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
if (Array.isArray(okdata)) {
//console.log("reloadSoundChannel : ", okdata)
window.soundChannels.push(...okdata);
fill_soundchanneltablebody(window.soundChannels);
} else console.log("reloadSoundChannel: okdata is not array");
}, (errdata) => {
alert("Error loading sound channels : " + errdata.message);
});
}
$(document).ready(function () { $(document).ready(function () {
console.log("soundchannel.js loaded successfully"); console.log("soundchannel.js loaded successfully");
@@ -58,7 +82,7 @@ $(document).ready(function () {
$btnEditSoundChannel.prop('disabled', true); $btnEditSoundChannel.prop('disabled', true);
let API_SoundChannel = "SoundChannel/"; let API_SoundChannel = "SoundChannel/";
$findsoundchannel.on('input', function () { $findsoundchannel.off('input').on('input', function () {
let searchTerm = $(this).val().toLowerCase(); let searchTerm = $(this).val().toLowerCase();
if (searchTerm.length==0){ if (searchTerm.length==0){
window.selectedSoundChannel = null; window.selectedSoundChannel = null;
@@ -83,20 +107,16 @@ $(document).ready(function () {
$soundchannelip.val(''); $soundchannelip.val('');
} }
reloadSoundChannel(API_SoundChannel, () => { reloadSoundChannel(API_SoundChannel);
fill_soundchanneltablebody(window.soundChannels); $btnReinitializeSoundChannel.off('click').on('click', () => {
});
$btnReinitializeSoundChannel.click(() => {
DoClear(API_SoundChannel, "SoundChannels", (okdata) => { DoClear(API_SoundChannel, "SoundChannels", (okdata) => {
reloadSoundChannel(API_SoundChannel, () => { reloadSoundChannel(API_SoundChannel);
fill_soundchanneltablebody(window.soundChannels); alert("Success clear sound channels: " + okdata.message);
alert("Success clear sound channels: " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error clear sound channels: " + errdata.message); alert("Error clear sound channels: " + errdata.message);
}); });
}); });
$btnEditSoundChannel.click(() => { $btnEditSoundChannel.off('click').on('click', () => {
if (selectedSoundChannel) { if (selectedSoundChannel) {
let cells = selectedSoundChannel.find('td'); let cells = selectedSoundChannel.find('td');
/** @type {SoundChannel} */ /** @type {SoundChannel} */
@@ -135,10 +155,8 @@ $(document).ready(function () {
fetchAPI(API_SoundChannel + "UpdateByIndex/" + newsc.index, "PATCH", {}, newsc, (okdata) => { fetchAPI(API_SoundChannel + "UpdateByIndex/" + newsc.index, "PATCH", {}, newsc, (okdata) => {
reloadSoundChannel(API_SoundChannel, () => { reloadSoundChannel(API_SoundChannel);
fill_soundchanneltablebody(window.soundChannels); alert("Success edit sound channel: " + okdata.message);
alert("Success edit sound channel: " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error edit sound channel: " + errdata.message); alert("Error edit sound channel: " + errdata.message);
}); });
@@ -152,16 +170,14 @@ $(document).ready(function () {
} }
} }
}); });
$btnExportSoundChannel.click(() => { $btnExportSoundChannel.off('click').on('click', () => {
DoExport(API_SoundChannel, "soundchannels.xlsx", {}); DoExport(API_SoundChannel, "soundchannels.xlsx", {});
}); });
$btnImportSoundChannel.click(() => { $btnImportSoundChannel.off('click').on('click', () => {
DoImport(API_SoundChannel, (okdata) => { DoImport(API_SoundChannel, (okdata) => {
reloadSoundChannel(API_SoundChannel, () => { reloadSoundChannel(API_SoundChannel);
fill_soundchanneltablebody(window.soundChannels); alert("Success import sound channels: " + okdata.message);
alert("Success import sound channels: " + okdata.message);
});
}, (errdata) => { }, (errdata) => {
alert("Error importing sound channels from XLSX: " + errdata.message); alert("Error importing sound channels from XLSX: " + errdata.message);
}); });

View File

@@ -4,9 +4,10 @@
* @property {string} username Username * @property {string} username Username
* @property {string} password Password (plain) * @property {string} password Password (plain)
* @property {string} location Location * @property {string} location Location
* @property {string} soundbank_tags Soundbank variable tags separated by semicolon ; * @property {string} airline_tags Airline variable tags (string) separated by semicolon ;
* @property {string} messagebank_ann_id Messagebank announcement ID separated by semicolon ; * @property {string} city_tags City variable tags (string) separated by semicolon ;
* @property {string} broadcastzones Broadcast zones separated by semicolon ; * @property {string} messagebank_ann_id Messagebank announcement ID (number) separated by semicolon ;
* @property {string} broadcastzones Broadcast zones description (string) separated by semicolon ;
*/ */
/** List of UserDB data loaded from server /** List of UserDB data loaded from server
@@ -20,6 +21,89 @@ window.userdb = [];
*/ */
window.selecteduserrow = null; window.selecteduserrow = null;
/**
* @typedef {Object} KeyValueMessage
* @property {string} key
* @property {string} value
*/
/**
* List of airline tags loaded from server
* @type {KeyValueMessage[]}
*/
window.airlinetags = [];
/**
* List of city tags loaded from server
* @type {KeyValueMessage[]}
*/
window.citytags = [];
/**
* List of message bank IDs loaded from server
* @type {KeyValueMessage[]}
*/
window.messagebankids = [];
/**
* List of broadcast zones description loaded from server
* @type {string[]}
*/
window.broadcastzones = [];
/**
* Get Messagebank ANN_IDs from server
*/
function get_messagebankids() {
window.messagebankids = [];
fetchAPI("MessageBank/" + "MessageIDs", "GET", {}, null, (okdata) => {
if (Array.isArray(okdata)) {
window.messagebankids.push(...okdata);
}
}, (errdata) => {
alert("Error loading message bank IDs : " + errdata.message);
});
}
/**
* Get Airline Tags from server
*/
function get_airlinetags() {
window.airlinetags = [];
fetchAPI("SoundBank/" + "AirlineTags", "GET", {}, null, (okdata) => {
if (Array.isArray(okdata)) {
window.airlinetags.push(...okdata);
}
}, (errdata) => {
alert("Error loading airline tags : " + errdata.message);
});
}
/**
* Get City Tags from server
*/
function get_citytags() {
window.citytags = [];
fetchAPI("SoundBank/" + "CityTags", "GET", {}, null, (okdata) => {
if (Array.isArray(okdata)) {
window.citytags.push(...okdata);
}
}, (errdata) => {
alert("Error loading city tags : " + errdata.message);
});
}
/**
* Get Broadcast Zones descriptions from server
*/
function get_broadcastzones_descriptions() {
window.broadcastzones = [];
fetchAPI("BroadcastZones/" + "BroadcastZoneDescriptions", "GET", {}, null, (okdata) => {
if (Array.isArray(okdata)) {
window.broadcastzones.push(...okdata);
}
}, (errdata) => {
alert("Error loading broadcast zones : " + errdata.message);
});
}
/** /**
* Fill user table body with values * Fill user table body with values
* @param {UserDB[]} vv values to fill * @param {UserDB[]} vv values to fill
@@ -34,14 +118,16 @@ function fill_usertablebody(vv) {
vv.forEach(item => { vv.forEach(item => {
const row = `<tr> const row = `<tr>
<td>${item.index}</td> <td>${item.index}</td>
<td>${item.datenya}</td> <td>${item.username}</td>
<td>${item.timenya}</td> <td>${item.location}</td>
<td>${item.machine}</td> <td>${item.airline_tags}</td>
<td>${item.description}</td> <td>${item.city_tags}</td>
<td>${item.messagebank_ann_id}</td>
<td>${item.broadcastzones}</td>
</tr>`; </tr>`;
$('#usertablebody').append(row); $('#usertablebody').append(row);
let $addedrow = $('#usertablebody tr:last'); let $addedrow = $('#usertablebody tr:last');
$addedrow.on('click', function () { $addedrow.off('click').on('click', function () {
if (window.selecteduserrow) { if (window.selecteduserrow) {
window.selecteduserrow.find('td').css('background-color', ''); window.selecteduserrow.find('td').css('background-color', '');
if (window.selecteduserrow.is($(this))) { if (window.selecteduserrow.is($(this))) {
@@ -63,9 +149,9 @@ function fill_usertablebody(vv) {
/** /**
* Reload UserDB from server with date and filter * Reload UserDB from server with date and filter
* @param {String} APIURL API URL endpoint , default "User/" * @param {String} APIURL API URL endpoint , default "UserManagement/"
*/ */
function reloaduserDB(APIURL = "User/") { function reloaduserDB(APIURL = "UserManagement/") {
window.userdb = []; window.userdb = [];
fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => { fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
if (Array.isArray(okdata)) { if (Array.isArray(okdata)) {
@@ -79,50 +165,395 @@ function reloaduserDB(APIURL = "User/") {
$(document).ready(function () { $(document).ready(function () {
console.log("usermanagement.js ready"); console.log("usermanagement.js ready");
let $usertablebody = $('#usertablebody'); get_airlinetags();
let $finduser = $('#finduser'); get_citytags();
let $btnClear = $('#btnClear'); get_messagebankids();
let $btnAdd = $('#btnAdd'); get_broadcastzones_descriptions();
let $btnRemove = $('#btnRemove');
let $btnEdit = $('#btnEdit');
let $btnExport = $('#btnExport');
let $btnImport = $('#btnImport');
let APIURL = "User/";
// add / edit modal elements let APIURL = "UserManagement/";
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 function clearAddModal() {
let $soundbankmodal = $('#soundbankmodal'); $('#modalindex').val("");
let $soundbankselection = $('#soundbankselection'); $('#modalusername').val("");
let $soundbankselectionsave = $('#soundbankselectionsave'); $('#modalpassword').val("");
let $soundbankselectionclose = $('#soundbankselectionclose'); $('#modalverifypassword').val("");
$('#modalairlinetags').val("");
$('#modalcitytags').val("");
$('#modalmessagebank').val("");
$('#modalbroadcastzones').val("");
$('#modallocation').val("");
}
function fill_citylist() {
$('#citylist').empty();
citytags.forEach(tag => {
let value = `${tag.value} [${tag.key}]`;
const row = `<div class="form-check">
<input class="form-check-input citytagcheckbox" type="checkbox" value="${tag.key}" id="citytag_${tag.key}">
<label class="form-check-label" for="citytag_${tag.key}">
${value}
</label>
</div>`;
$('#citylist').append(row);
});
}
function fill_airlinelist() {
$('#airlinelist').empty();
airlinetags.forEach(tag => {
let value = `${tag.value} [${tag.key}]`;
const row = `<div class="form-check">
<input class="form-check-input airlinetagcheckbox" type="checkbox" value="${tag.key}" id="airlinetag_${tag.key}">
<label class="form-check-label" for="airlinetag_${tag.key}">
${value}
</label>
</div>`;
$('#airlinelist').append(row);
});
}
// broadcast zone selection modal elements // broadcast zone selection modal elements
let $broadcastzonemodal = $('#broadcastzonemodal');
let $broadcastzoneselection = $('#broadcastzoneselection'); function fill_broadcastzonelist() {
let $broadcastzoneselectionsave = $('#broadcastzoneselectionsave'); $('#broadcastzonelist').empty();
let $broadcastzoneselectionclose = $('#broadcastzoneselectionclose'); broadcastzones.forEach(desc => {
const row = `<div class="form-check">
<input class="form-check-input broadcastzonecheckbox" type="checkbox" value="${desc}" id="broadcastzone_${desc}">
<label class="form-check-label" for="broadcastzone_${desc}">
${desc}
</label>
</div>`;
$('#broadcastzonelist').append(row);
});
}
// messagebank selection modal elements // messagebank selection modal elements
let $messagebankmodal = $('#messagebankmodal');
let $messagebankselection = $('#messagebankselection');
let $messagebankselectionsave = $('#messagebankselectionsave');
let $messagebankselectionclose = $('#messagebankselectionclose');
$usertablebody.empty(); function fill_messagebanklist() {
$finduser.on('input', function () { $('#messagebanklist').empty();
messagebankids.forEach(id => {
let value = `${id.value} [${id.key}]`;
const row = `<div class="form-check">
<input class="form-check-input messagebankidcheckbox" type="checkbox" value="${id.key}" id="messagebankid_${id.key}">
<label class="form-check-label" for="messagebankid_${id.key}">
${value}
</label>
</div>`;
$('#messagebanklist').append(row);
});
}
$('#usertablebody').empty();
reloaduserDB();
$('#finduser').off('input').on('input', function () {
let searchTerm = $(this).val().toLowerCase();
if (searchTerm.length > 0) {
let filteredUsers = window.userdb.filter(user =>
user.username.toLowerCase().includes(searchTerm) ||
user.airline_tags.toLowerCase().includes(searchTerm) ||
user.city_tags.toLowerCase().includes(searchTerm)
//user.messagebank_ann_id.toLowerCase().includes(searchTerm) ||
//user.broadcastzones.toLowerCase().includes(searchTerm)
);
fill_usertablebody(filteredUsers);
} else {
fill_usertablebody(window.userdb);
}
});
/**
* Show modal dialog for soundbank, messagebank, broadcastzone selection
* @param {boolean} editmode if true, edit mode, else add mode
* @param {number} index index of user to edit, default 0
*/
function modalshow(editmode = false, index=0) {
// event on click btnShowSoundbankModal
$('#btnShowSoundbankModal').off('click').on('click', function () {
$('#soundbankmodal').modal('show');
fill_citylist();
fill_airlinelist();
let airline = $('#modalairlinetags').val().trim();
let city = $('#modalcitytags').val().trim();
if (airline.length > 0) {
let airlinekeys = airline.split(";");
$('#airlinelist input[type=checkbox]').each(function () {
let tag = $(this).val();
if (airlinekeys.includes(tag)) {
$(this).prop('checked', true);
}
});
}
if (city.length > 0) {
let citykeys = city.split(";");
$('#citylist input[type=checkbox]').each(function () {
let tag = $(this).val();
if (citykeys.includes(tag)) {
$(this).prop('checked', true);
}
});
}
$('#soundbankmodal').off('click.soundbankselectionsave').on('click.soundbankselectionsave', '#soundbankselectionsave', function () {
let selected_airlinetags = [];
$('#airlinelist input[type=checkbox]:checked').each(function () {
selected_airlinetags.push($(this).val());
});
let selected_citytags = [];
$('#citylist input[type=checkbox]:checked').each(function () {
selected_citytags.push($(this).val());
});
//console.log("Selected airline tags: ", selected_airlinetags);
//console.log("Selected city tags: ", selected_citytags);
if (selected_airlinetags.length == 0 || selected_citytags.length == 0) {
alert("Please select at least one airline tag and one city tag.");
return;
}
let airlinevalue = selected_airlinetags.join(";");
let cityvalue = selected_citytags.join(";");
$('#modalairlinetags').val(airlinevalue);
$('#modalcitytags').val(cityvalue);
$('#soundbankmodal').modal('hide');
});
$('#soundbankmodal').off('click.soundbankselectionclose').on('click.soundbankselectionclose', '#soundbankselectionclose', function () {
$('#soundbankmodal').modal('hide');
});
});
// event on click btnShowMessagebankModal
$('#btnShowMessagebankModal').off('click').on('click', function () {
$('#messagebankmodal').modal('show');
fill_messagebanklist();
let messagebank = $('#modalmessagebank').val().trim();
if (messagebank.length > 0) {
let messagebankkeys = messagebank.split(";");
$('#messagebanklist input[type=checkbox]').each(function () {
let id = $(this).val();
if (messagebankkeys.includes(id)) {
$(this).prop('checked', true);
}
});
}
$('#messagebankmodal').off('click.messagebankselectionsave').on('click.messagebankselectionsave', '#messagebankselectionsave', function () {
let selected_messagebankids = [];
$('#messagebanklist input[type=checkbox]:checked').each(function () {
selected_messagebankids.push($(this).val());
});
//console.log("Selected message bank IDs: ", selected_messagebankids);
if (selected_messagebankids.length == 0) {
alert("Please select at least one message bank ID.");
return;
}
let messagebankvalue = selected_messagebankids.join(";");
$('#modalmessagebank').val(messagebankvalue);
$('#messagebankmodal').modal('hide');
});
$('#messagebankmodal').off('click.messagebankselectionclose').on('click.messagebankselectionclose', '#messagebankselectionclose', function () {
$('#messagebankmodal').modal('hide');
});
});
// event on click btnShowBroaadcastZoneModal
$('#btnShowBroaadcastZoneModal').off('click').on('click', function () {
$('#broadcastzonemodal').modal('show');
fill_broadcastzonelist();
let broadcastzones = $('#modalbroadcastzones').val().trim();
if (broadcastzones.length > 0) {
let broadcastzonesvalues = broadcastzones.split(";");
$('#broadcastzonelist input[type=checkbox]').each(function () {
let desc = $(this).val();
if (broadcastzonesvalues.includes(desc)) {
$(this).prop('checked', true);
}
});
}
$('#broadcastzonemodal').off('click.broadcastzoneselectionsave').on('click.broadcastzoneselectionsave', '#broadcastzoneselectionsave', function () {
let selected_broadcastzones = [];
$('#broadcastzonelist input[type=checkbox]:checked').each(function () {
selected_broadcastzones.push($(this).val());
});
//console.log("Selected broadcast zones: ", selected_broadcastzones);
if (selected_broadcastzones.length == 0) {
alert("Please select at least one broadcast zone.");
return;
}
let broadcastzonesvalue = selected_broadcastzones.join(";");
$('#modalbroadcastzones').val(broadcastzonesvalue);
$('#broadcastzonemodal').modal('hide');
});
$('#broadcastzonemodal').off('click.broadcastzoneselectionclose').on('click.broadcastzoneselectionclose', '#broadcastzoneselectionclose', function () {
$('#broadcastzonemodal').modal('hide');
});
});
// event on Click save button
$('#addmodal').off('click.usermanagementsave').on('click.usermanagementsave', '#usermanagementsave', function () {
let username = $('#modalusername').val().trim();
let password = $('#modalpassword').val();
let verifypassword = $('#modalverifypassword').val();
let location = $('#modallocation').val().trim();
let airline_tags = $('#modalairlinetags').val().trim();
let city_tags = $('#modalcitytags').val().trim();
let messagebank_ann_id = $('#modalmessagebank').val().trim();
let broadcastzones = $('#modalbroadcastzones').val().trim();
if (username.length === 0) {
alert("Username cannot be empty");
return;
}
if (password.length === 0 || verifypassword.length === 0) {
alert("Password cannot be empty");
return;
}
if (password !== verifypassword) {
alert("Password and Verify Password do not match");
return;
}
if (airline_tags.length === 0) {
alert("Airline tags cannot be empty");
return;
}
if (city_tags.length === 0) {
alert("City tags cannot be empty");
return;
}
if (messagebank_ann_id.length === 0) {
alert("Message bank ANN_ID cannot be empty");
return;
}
if (broadcastzones.length === 0) {
alert("Broadcast zones cannot be empty");
return;
}
/**
* @type {UserDB}
*/
let ll = {
index: index,
username: username,
password: password,
location: location,
airline_tags: airline_tags,
city_tags: city_tags,
messagebank_ann_id: messagebank_ann_id,
broadcastzones: broadcastzones
}
if (editmode) {
fetchAPI(APIURL + "UpdateByIndex/" + index, "PATCH", {}, ll, (okdata) => {
alert("Success update User : " + okdata.message);
reloaduserDB();
}, (errdata) => {
alert("Error update User : " + errdata.message);
});
} else {
fetchAPI(APIURL + "Add", "POST", {}, ll, (okdata) => {
alert("Success add User : " + okdata.message);
reloaduserDB();
}, (errdata) => {
alert("Error add User : " + errdata.message);
});
}
$('#addmodal').modal('hide');
});
// event on Click close button
$('#addmodal').off('click.usermanagementclose').on('click.usermanagementclose', '#usermanagementclose', function () {
$('#addmodal').modal('hide');
});
}
$('#btnClear').off('click').on('click', function () {
DoClear(APIURL, "UserManagement", (okdata) => {
reloaduserDB();
alert("Success clear user management : " + okdata.message);
}, (errdata) => {
alert("Error clear user management : " + errdata.message);
});
});
$('#btnAdd').off('click').on('click', () => {
$('#addmodal').modal('show');
clearAddModal();
modalshow(false,0);
});
$('#btnRemove').off('click').on('click', () => {
if (window.selecteduserrow) {
let cells = window.selecteduserrow.find('td');
/** @type {UserDB} */
let user = {
index: parseInt(cells.eq(0).text()),
username: cells.eq(1).text(),
password: cells.eq(2).text(),
airline_tags: cells.eq(3).text(),
city_tags: cells.eq(4).text(),
messagebank_ann_id: cells.eq(5).text(),
broadcastzones: cells.eq(6).text()
}
if (confirm(`Are you sure to delete user [${user.index}] Username=${user.username} ?`)) {
fetchAPI(APIURL + "DeleteByIndex/" + user.index, "DELETE", {}, null, (okdata) => {
reloaduserDB();
alert("Success delete user : " + okdata.message);
}, (errdata) => {
alert("Error delete user : " + errdata.message);
});
}
} else {
alert("No user selected");
}
});
$('#btnEdit').off('click').on('click', () => {
if (window.selecteduserrow) {
let cells = window.selecteduserrow.find('td');
let index = parseInt(cells.eq(0).text());
if (isNaN(index) || index <= 0) {
alert("Invalid user index");
return;
}
/** @type {UserDB} */
let user = window.userdb.find(u => u.index === index);
if (!user) {
alert("User not found");
return;
}
if (confirm(`Are you sure to edit user [${user.index}] Username=${user.username} ?`)) {
$('#addmodal').modal('show');
// fill modal with user data
$('#modalindex').val(user.index);
$('#modalusername').val(user.username);
$('#modalpassword').val(user.password);
$('#modalverifypassword').val(user.password);
$('#modallocation').val(user.location);
$('#modalairlinetags').val(user.airline_tags);
$('#modalcitytags').val(user.city_tags);
$('#modalmessagebank').val(user.messagebank_ann_id);
$('#modalbroadcastzones').val(user.broadcastzones);
modalshow(true, user.index);
}
} else {
alert("No user selected");
}
});
$('#btnExport').off('click').on('click', () => {
DoExport(APIURL, "user.xlsx", {});
});
$('#btnImport').off('click').on('click', () => {
DoImport(APIURL, (okdata) => {
reloaduserDB();
alert("Success import user : " + okdata.message);
}, (errdata) => {
alert("Error importing user from XLSX : " + errdata.message);
});
}); });
}); });

View File

@@ -4,8 +4,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>AAS_NewGen_30Sept25</title> <title>AAS_NewGen_08OKT25</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css"> <link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css"> <link rel="stylesheet" href="assets/css/styles.css">
</head> </head>
@@ -19,15 +20,15 @@
<div class="modal fade" role="dialog" tabindex="-1" id="broadcastzonemodal"> <div class="modal fade" role="dialog" tabindex="-1" id="broadcastzonemodal">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header bg-header-modal">
<h4 class="modal-title">Add / Edit Broadcast Zones</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button> <h4 class="modal-title">Add / Edit Broadcast Zones</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body bg-modal-body">
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p class="text-add">Index</p> <p class="text-add">Index</p>
</div> </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 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" readonly=""></div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
@@ -79,7 +80,7 @@
<div class="form-check"><input class="form-check-input" type="checkbox" id="R08"><label class="form-check-label" for="formCheck-8">08</label></div> <div class="form-check"><input class="form-check-input" type="checkbox" id="R08"><label class="form-check-label" for="formCheck-8">08</label></div>
</div> </div>
</div> </div>
<div class="col-3"> <div class="col-3 invisible">
<div class="row"> <div class="row">
<div class="form-check"><input class="form-check-input" type="checkbox" id="R09"><label class="form-check-label" for="formCheck-25">09</label></div> <div class="form-check"><input class="form-check-input" type="checkbox" id="R09"><label class="form-check-label" for="formCheck-25">09</label></div>
</div> </div>
@@ -105,7 +106,7 @@
<div class="form-check"><input class="form-check-input" type="checkbox" id="R16"><label class="form-check-label" for="formCheck-32">16</label></div> <div class="form-check"><input class="form-check-input" type="checkbox" id="R16"><label class="form-check-label" for="formCheck-32">16</label></div>
</div> </div>
</div> </div>
<div class="col-3"> <div class="col-3 invisible">
<div class="row"> <div class="row">
<div class="form-check"><input class="form-check-input" type="checkbox" id="R17"><label class="form-check-label" for="formCheck-17">17</label></div> <div class="form-check"><input class="form-check-input" type="checkbox" id="R17"><label class="form-check-label" for="formCheck-17">17</label></div>
</div> </div>
@@ -131,7 +132,7 @@
<div class="form-check"><input class="form-check-input" type="checkbox" id="R24"><label class="form-check-label" for="formCheck-24">24</label></div> <div class="form-check"><input class="form-check-input" type="checkbox" id="R24"><label class="form-check-label" for="formCheck-24">24</label></div>
</div> </div>
</div> </div>
<div class="col-3"> <div class="col-3 invisible">
<div class="row"> <div class="row">
<div class="form-check"><input class="form-check-input" type="checkbox" id="R25"><label class="form-check-label" for="formCheck-9">25</label></div> <div class="form-check"><input class="form-check-input" type="checkbox" id="R25"><label class="form-check-label" for="formCheck-9">25</label></div>
</div> </div>
@@ -254,10 +255,10 @@
<div class="modal fade" role="dialog" tabindex="-1" id="soundchannelmodal"> <div class="modal fade" role="dialog" tabindex="-1" id="soundchannelmodal">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header bg-header-modal">
<h4 class="modal-title">Edit Sound Channel</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button> <h4 class="modal-title">Edit Sound Channel</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body bg-modal-body">
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p class="text-add">Index</p> <p class="text-add">Index</p>
@@ -268,13 +269,13 @@
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p class="text-add">Description</p> <p class="text-add">Description</p>
</div> </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 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" readonly=""></div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p class="text-add">IP Address</p> <p class="text-add">IP Address</p>
</div> </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 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" required="" inputmode="numeric" pattern="^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$"></div>
</div> </div>
</div> </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 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>
@@ -282,6 +283,7 @@
</div> </div>
</div> </div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script> <script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/soundchannel.js"></script> <script src="assets/js/soundchannel.js"></script>
<script src="assets/js/broadcastzones.js"></script> <script src="assets/js/broadcastzones.js"></script>
</body> </body>

View File

@@ -6,6 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>AAS NewGeneration 17092025</title> <title>AAS NewGeneration 17092025</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/select2.min.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css"> <link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css"> <link rel="stylesheet" href="assets/css/styles.css">
</head> </head>
@@ -97,9 +99,10 @@
</div> </div>
<div class="container w-100 pad-container" id="content"></div> <div class="container w-100 pad-container" id="content"></div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script> <script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/jquery-3.7.1.min.js"></script> <script src="assets/js/jquery-3.7.1.min.js"></script>
<script src="assets/js/select2.js"></script>
<script src="assets/js/script.js"></script> <script src="assets/js/script.js"></script>
<script src="assets/js/select2.min.js"></script>
</body> </body>
</html> </html>

View File

@@ -4,8 +4,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>AAS_NewGen_30Sept25</title> <title>AAS_NewGen_08OKT25</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css"> <link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css"> <link rel="stylesheet" href="assets/css/styles.css">
</head> </head>
@@ -53,10 +54,10 @@
<div class="modal fade" role="dialog" tabindex="-1" id="languagemodal"> <div class="modal fade" role="dialog" tabindex="-1" id="languagemodal">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header bg-header-modal">
<h4 class="modal-title">Add / Edit Language</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button> <h4 class="modal-title">Add / Edit Language</h4><button class="btn-close" type="button" aria-label="Close" data-bs-dismiss="modal"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body bg-modal-body">
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p class="text-add">Index</p> <p class="text-add">Index</p>
@@ -88,6 +89,7 @@
</div> </div>
</div> </div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script> <script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/languagelink.js"></script> <script src="assets/js/languagelink.js"></script>
</body> </body>

View File

@@ -4,8 +4,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>AAS_NewGen_30Sept25</title> <title>AAS_NewGen_08OKT25</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css"> <link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css"> <link rel="stylesheet" href="assets/css/styles.css">
</head> </head>
@@ -50,6 +51,7 @@
</div> </div>
</div> </div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script> <script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/log.js"></script> <script src="assets/js/log.js"></script>
</body> </body>

View File

@@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>AAS NewGeneration 17092025</title> <title>AAS NewGeneration 17092025</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css"> <link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css"> <link rel="stylesheet" href="assets/css/styles.css">
</head> </head>
@@ -37,6 +38,7 @@
</div> </div>
</section> </section>
<script src="assets/bootstrap/js/bootstrap.min.js"></script> <script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
</body> </body>
</html> </html>

View File

@@ -4,8 +4,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>AAS_NewGen_30Sept25</title> <title>AAS_NewGen_08OKT25</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css"> <link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css"> <link rel="stylesheet" href="assets/css/styles.css">
</head> </head>
@@ -55,29 +56,29 @@
</div> </div>
</div> </div>
<div class="modal fade" role="dialog" tabindex="-1" id="messagebankmodal"> <div class="modal fade" role="dialog" tabindex="-1" id="messagebankmodal">
<div class="modal-dialog" role="document"> <div class="modal-dialog modal-xl" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header bg-header-modal">
<h4 class="modal-title">Add / Edit Message</h4> <h4 class="modal-title">Add / Edit Message</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body bg-modal-body">
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3">
<p class="text-add">Index</p> <p class="text-add">Index</p>
</div> </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 class="col-8 col-sm-8 col-md-8 col-lg-9 col-xl-9"><input class="w-25 input-add form-control" type="text" id="messageindex" placeholder="Index" readonly=""></div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3">
<p class="text-add">Description</p> <p class="text-add">Description</p>
</div> </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 class="col-8 col-sm-8 col-md-8 col-lg-9 col-xl-9"><input type="text" id="messagedescription" class="input-add form-control" placeholder="Description"></div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3">
<p class="text-add">Language</p> <p class="text-add">Language</p>
</div> </div>
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="messagelanguage" class="input-add form-control"> <div class="col-8 col-sm-8 col-md-8 col-lg-9 col-xl-9"><select id="messagelanguage" class="input-add form-control">
<option value="INDONESIA">Indonesia</option> <option value="INDONESIA">Indonesia</option>
<option value="LOCAL">Local</option> <option value="LOCAL">Local</option>
<option value="ENGLISH">English</option> <option value="ENGLISH">English</option>
@@ -87,40 +88,40 @@
</select></div> </select></div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3">
<p class="text-add">ANN ID</p> <p class="text-add">ANN ID</p>
</div> </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 class="col-8 col-sm-8 col-md-8 col-lg-9 col-xl-9"><input type="number" id="messageannid" class="input-add form-control" min="1" max="100" value="1" step="1" placeholder="Announcement ID"></div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-3 col-xl-3">
<p class="text-add">Voice Type</p> <p class="text-add">Voice Type</p>
</div> </div>
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="messagevoicetype" class="input-add form-control"> <div class="col-8 col-sm-8 col-md-8 col-lg-9 col-xl-9"><select id="messagevoicetype" class="input-add form-control">
<option value="VOICE_1">Voice 1</option> <option value="VOICE_1">Voice 1</option>
<option value="VOICE_2">Voice 2</option> <option value="VOICE_2">Voice 2</option>
<option value="VOICE_3">Voice 3</option> <option value="VOICE_3">Voice 3</option>
</select></div> </select></div>
</div> </div>
<div class="row"> <div class="row">
<div class="col"> <div class="col bg-light">
<ul class="list-unstyled w-100 h-100" id="messageavailablevariables"></ul> <ul class="list-unstyled w-100 h-100" id="messageavailablevariables"></ul>
</div> </div>
<div class="col-2"> <div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2">
<div class="row"><button class="btn btn-danger" id="btnclearlist" type="button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;"> <div class="row pad-row-btn"><button class="btn btn-round-basic color-remove" data-bs-toggle="tooltip" data-bss-tooltip="" id="btnclearlist" type="button" title="Clear List"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;">
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. --> <!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
<path d="M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c-9.4 9.4-9.4 24.6 0 33.9l47 47-47 47c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l47-47 47 47c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-47-47 47-47c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0l-47 47-47-47c-9.4-9.4-24.6-9.4-33.9 0z"></path> <path d="M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c-9.4 9.4-9.4 24.6 0 33.9l47 47-47 47c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l47-47 47 47c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-47-47 47-47c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0l-47 47-47-47c-9.4-9.4-24.6-9.4-33.9 0z"></path>
</svg></button></div> </svg></button></div>
<div class="row"><button class="btn btn-warning" id="btnremovefromlist" type="button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;"> <div class="row pad-row-btn"><button class="btn btn-round-basic color-edit" data-bs-toggle="tooltip" data-bss-tooltip="" data-bs-placement="right" id="btnremovefromlist" type="button" title="Remove"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;">
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. --> <!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
<path d="M48 256a208 208 0 1 1 416 0A208 208 0 1 1 48 256zm464 0A256 256 0 1 0 0 256a256 256 0 1 0 512 0zM217.4 376.9c4.2 4.5 10.1 7.1 16.3 7.1c12.3 0 22.3-10 22.3-22.3V304h96c17.7 0 32-14.3 32-32V240c0-17.7-14.3-32-32-32H256V150.3c0-12.3-10-22.3-22.3-22.3c-6.2 0-12.1 2.6-16.3 7.1L117.5 242.2c-3.5 3.8-5.5 8.7-5.5 13.8s2 10.1 5.5 13.8l99.9 107.1z"></path> <path d="M48 256a208 208 0 1 1 416 0A208 208 0 1 1 48 256zm464 0A256 256 0 1 0 0 256a256 256 0 1 0 512 0zM217.4 376.9c4.2 4.5 10.1 7.1 16.3 7.1c12.3 0 22.3-10 22.3-22.3V304h96c17.7 0 32-14.3 32-32V240c0-17.7-14.3-32-32-32H256V150.3c0-12.3-10-22.3-22.3-22.3c-6.2 0-12.1 2.6-16.3 7.1L117.5 242.2c-3.5 3.8-5.5 8.7-5.5 13.8s2 10.1 5.5 13.8l99.9 107.1z"></path>
</svg></button></div> </svg></button></div>
<div class="row"><button class="btn btn-success" id="btnaddtolist" type="button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;"> <div class="row pad-row-btn"><button class="btn btn-round-basic color-import" data-bs-toggle="tooltip" data-bss-tooltip="" data-bs-placement="right" id="btnaddtolist" type="button" title="Add"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;">
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. --> <!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
<path d="M464 256A208 208 0 1 1 48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM294.6 135.1c-4.2-4.5-10.1-7.1-16.3-7.1C266 128 256 138 256 150.3V208H160c-17.7 0-32 14.3-32 32v32c0 17.7 14.3 32 32 32h96v57.7c0 12.3 10 22.3 22.3 22.3c6.2 0 12.1-2.6 16.3-7.1l99.9-107.1c3.5-3.8 5.5-8.7 5.5-13.8s-2-10.1-5.5-13.8L294.6 135.1z"></path> <path d="M464 256A208 208 0 1 1 48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM294.6 135.1c-4.2-4.5-10.1-7.1-16.3-7.1C266 128 256 138 256 150.3V208H160c-17.7 0-32 14.3-32 32v32c0 17.7 14.3 32 32 32h96v57.7c0 12.3 10 22.3 22.3 22.3c6.2 0 12.1-2.6 16.3-7.1l99.9-107.1c3.5-3.8 5.5-8.7 5.5-13.8s-2-10.1-5.5-13.8L294.6 135.1z"></path>
</svg></button></div> </svg></button></div>
</div> </div>
<div class="col"> <div class="col bg-light">
<ul class="list-unstyled w-100 h-100" id="messageselectedvariables"></ul> <ul class="list-unstyled w-100 h-100" id="messageselectedvariables"></ul>
</div> </div>
</div> </div>
@@ -130,6 +131,7 @@
</div> </div>
</div> </div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script> <script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/messagebank.js"></script> <script src="assets/js/messagebank.js"></script>
</body> </body>

View File

@@ -4,8 +4,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>AAS_NewGen_30Sept25</title> <title>AAS_NewGen_08OKT25</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css"> <link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css"> <link rel="stylesheet" href="assets/css/styles.css">
</head> </head>
@@ -1272,9 +1273,9 @@
</div> </div>
</div> </div>
</div> </div>
<div class="accordion-item pad-accordion"> <div class="accordion-item pad-accordion pad-2">
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed bg-heading2" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-2" aria-expanded="false" aria-controls="accordion-1 .item-2">Paging Queue</button></h2> <h2 class="accordion-header" role="tab"><button class="accordion-button collapsed bg-heading2" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-2" aria-expanded="false" aria-controls="accordion-1 .item-2">Paging Queue</button></h2>
<div class="accordion-collapse collapse item-2" role="tabpanel" data-bs-parent="#accordion-1"> <div class="accordion-collapse collapse item-2 pad-2" role="tabpanel" data-bs-parent="#accordion-1">
<div class="accordion-body"> <div class="accordion-body">
<div class="row"> <div class="row">
<div class="table-responsive"> <div class="table-responsive">
@@ -1291,6 +1292,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col"><button class="btn w-100 pad-button btn-round-basic color-add" id="clearpagingqueue" type="button">Clear Queue</button></div> <div class="col"><button class="btn w-100 pad-button btn-round-basic color-add" id="clearpagingqueue" type="button">Clear Queue</button></div>
<div class="col"><button class="btn w-100 pad-button btn-round-basic color-remove" id="removepagingqueue" type="button">Remove Queue</button></div>
</div> </div>
</div> </div>
</div> </div>
@@ -1314,12 +1316,14 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col"><button class="btn w-100 pad-button btn-round-basic color-add" id="clearautomatictable" type="button">Clear Queue</button></div> <div class="col"><button class="btn w-100 pad-button btn-round-basic color-add" id="clearautomatictable" type="button">Clear Queue</button></div>
<div class="col"><button class="btn w-100 pad-button btn-round-basic color-remove" id="removeautomatictable" type="button">Remove Queue</button></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script> <script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
</body> </body>
</html> </html>

View File

@@ -4,8 +4,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>AAS_NewGen_30Sept25</title> <title>AAS_NewGen_08OKT25</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css"> <link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css"> <link rel="stylesheet" href="assets/css/styles.css">
</head> </head>
@@ -17,6 +18,7 @@
</div> </div>
</div> </div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script> <script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
</body> </body>
</html> </html>

View File

@@ -4,8 +4,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>AAS_NewGen_30Sept25</title> <title>AAS_NewGen_08OKT25</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css"> <link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css"> <link rel="stylesheet" href="assets/css/styles.css">
</head> </head>
@@ -57,10 +58,10 @@
<div class="modal fade border-0" role="dialog" tabindex="-1" id="soundbankmodal"> <div class="modal fade border-0" role="dialog" tabindex="-1" id="soundbankmodal">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header show"> <div class="modal-header show bg-header-modal">
<h4 class="modal-title align-content-center">Add / Edit Soundbank</h4> <h4 class="modal-title align-content-center">Add / Edit Soundbank</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body bg-modal-body">
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p class="text-add">Index</p> <p class="text-add">Index</p>
@@ -97,11 +98,11 @@
</div> </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 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>
<div class="row" style="height: 100px;"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p class="text-add">Path</p> <p class="text-add">Path</p>
</div> </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 class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><select id="modalpath" class="input-add form-select" name="modalpath" data-control="select2"></select></div>
</div> </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> <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>
@@ -109,6 +110,7 @@
</div> </div>
</div> </div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script> <script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/soundbank.js"></script> <script src="assets/js/soundbank.js"></script>
</body> </body>

View File

@@ -4,8 +4,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>AAS_NewGen_30Sept25</title> <title>AAS_NewGen_08OKT25</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css"> <link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css"> <link rel="stylesheet" href="assets/css/styles.css">
</head> </head>
@@ -29,6 +30,7 @@
</div> </div>
</div> </div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script> <script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
</body> </body>
</html> </html>

View File

@@ -4,8 +4,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>AAS_NewGen_30Sept25</title> <title>AAS_NewGen_08OKT25</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css"> <link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css"> <link rel="stylesheet" href="assets/css/styles.css">
</head> </head>
@@ -58,10 +59,10 @@
<div class="modal fade" role="dialog" tabindex="-1" id="schedulemodal"> <div class="modal fade" role="dialog" tabindex="-1" id="schedulemodal">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header bg-header-modal">
<h4 class="modal-title">Add / Edit Schedule</h4> <h4 class="modal-title">Add / Edit Schedule</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body bg-modal-body">
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p class="text-add">Index</p> <p class="text-add">Index</p>
@@ -148,7 +149,7 @@
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p class="text-add">Sound Path</p> <p class="text-add">Sound Path</p>
</div> </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 class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="schedulesoundpath" class="input-add form-control"></div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
@@ -174,6 +175,7 @@
</div> </div>
</div> </div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script> <script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/schedulebank.js"></script> <script src="assets/js/schedulebank.js"></script>
</body> </body>

View File

@@ -4,8 +4,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>AAS_NewGen_30Sept25</title> <title>AAS_NewGen_08OKT25</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bss-overrides.css">
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css"> <link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
<link rel="stylesheet" href="assets/css/styles.css"> <link rel="stylesheet" href="assets/css/styles.css">
</head> </head>
@@ -44,9 +45,10 @@
<th class="col-sm-1">No</th> <th class="col-sm-1">No</th>
<th class="col-sm-1">Username</th> <th class="col-sm-1">Username</th>
<th class="col-sm-1">Location</th> <th class="col-sm-1">Location</th>
<th class="col">Soundbank</th> <th class="col-sm-2">Airline</th>
<th class="col-sm-2">City</th>
<th class="col-sm-2">Messagebank</th> <th class="col-sm-2">Messagebank</th>
<th class="col-sm-2">Broadcast Zones</th> <th class="col">Broadcast Zones</th>
</tr> </tr>
</thead> </thead>
<tbody id="usertablebody"></tbody> <tbody id="usertablebody"></tbody>
@@ -56,10 +58,10 @@
<div class="modal fade border-0" role="dialog" tabindex="-1" id="addmodal"> <div class="modal fade border-0" role="dialog" tabindex="-1" id="addmodal">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header show"> <div class="modal-header text-start show bg-header-modal">
<h4 class="modal-title align-content-center">Add / Edit User</h4> <h4 class="modal-title text-center align-content-center">Add / Edit User</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body bg-modal-body">
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p class="text-add">Index</p> <p class="text-add">Index</p>
@@ -86,21 +88,53 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p class="text-add">Sound Bank</p> <p class="text-add">Location</p>
</div>
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="modallocation" 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">Airline Tags</p>
</div>
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8">
<div class="row">
<div class="col-8"><input type="text" id="modalairlinetags" class="form-control input-add"></div>
<div class="col-4"><button class="btn w-100 btn-select btn-round-basic color-import" id="btnShowSoundbankModal" type="button">Select</button></div>
</div>
</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">City Tags</p>
</div>
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8">
<div class="row">
<div class="col-8"><input type="text" id="modalcitytags" class="form-control input-add"></div>
<div class="col-4"></div>
</div>
</div> </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>
<div class="row"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p class="text-add">Message Bank</p> <p class="text-add">Message Bank</p>
</div> </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 class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8">
<div class="row">
<div class="col-8"><input type="text" id="modalmessagebank" class="form-control input-add"></div>
<div class="col-4"><button class="btn w-100 btn-round-basic color-import" id="btnShowMessagebankModal" type="button">Select</button></div>
</div>
</div>
</div> </div>
<div class="row" style="height: 100px;"> <div class="row">
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"> <div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4">
<p class="text-add">Broadcast Zones</p> <p class="text-add">Broadcast Zones</p>
</div> </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 class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8">
<div class="row">
<div class="col-8 col-sm-8 col-md-8 col-lg-8 col-xl-8"><input type="text" id="modalbroadcastzones" class="form-control input-add" name="modalpath"></div>
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"><button class="btn w-100 btn-round-basic color-import" id="btnShowBroaadcastZoneModal" type="button">Select</button></div>
</div>
</div>
</div> </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 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>
@@ -110,43 +144,61 @@
<div class="modal fade" role="dialog" tabindex="-1" id="soundbankmodal"> <div class="modal fade" role="dialog" tabindex="-1" id="soundbankmodal">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header form-control input-add">
<h4 class="modal-title">Sound Bank Selection</h4> <h4 class="modal-title">Sound Bank Selection</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body bg-modal-body">
<p>(mon, ganti list checkbox, dengan id=soundbankselection</p> <div class="accordion" role="tablist" id="accordion-1">
<div class="accordion-item pad-accordion">
<h2 class="accordion-header" role="tab"><button class="accordion-button bg-heading1" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-1" aria-expanded="true" aria-controls="accordion-1 .item-1">Airline</button></h2>
<div class="accordion-collapse collapse show item-1" role="tabpanel" data-bs-parent="#accordion-1">
<div class="accordion-body">
<ul id="airlinelist"></ul>
</div>
</div>
</div>
<div class="accordion-item pad-accordion">
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed bg-heading2" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-2" aria-expanded="false" aria-controls="accordion-1 .item-2">City</button></h2>
<div class="accordion-collapse collapse item-2" role="tabpanel" data-bs-parent="#accordion-1">
<div class="accordion-body">
<ul id="citylist"></ul>
</div>
</div>
</div>
</div>
</div> </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 class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="soundbankselectionclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="soundbankselectionsave" type="button">Save</button></div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal fade" role="dialog" tabindex="-1" id="broadcastzonemodal"> <div class="modal fade" role="dialog" tabindex="-1" id="broadcastzonemodal">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header form-control input-add">
<h4 class="modal-title">Broadcast Zones Selection</h4> <h4 class="modal-title">Broadcast Zones Selection</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body bg-modal-body">
<p>(mon, ganti list checkbox, dengan id=broadcastzoneselection</p> <ul id="broadcastzonelist"></ul>
</div> </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 class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="broadcastzoneselectionclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="broadcastzoneselectionsave" type="button">Save</button></div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal fade" role="dialog" tabindex="-1" id="messagebankmodal"> <div class="modal fade" role="dialog" tabindex="-1" id="messagebankmodal">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header form-control input-add">
<h4 class="modal-title">Message Bank Selection</h4> <h4 class="modal-title">Message Bank Selection</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body bg-modal-body">
<p>(mon, ganti list checkbox, dengan id=messagebankselection</p> <ul id="messagebanklist"></ul>
</div> </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 class="modal-footer"><button class="btn btn-round-basic color-edit class25" id="messagebankselectionclose" type="button" data-bs-dismiss="modal">Close</button><button class="btn btn-round-basic color-add class25" id="messagebankselectionsave" type="button">Save</button></div>
</div> </div>
</div> </div>
</div> </div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script> <script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/usermanagement.js"></script> <script src="assets/js/usermanagement.js"></script>
</body> </body>

View File

@@ -7,6 +7,7 @@ import barix.TCP_Barix_Command_Server
import codes.Somecodes import codes.Somecodes
import com.sun.jna.Platform import com.sun.jna.Platform
import commandServer.TCP_Android_Command_Server import commandServer.TCP_Android_Command_Server
import content.Category
import content.Language import content.Language
import content.VoiceType import content.VoiceType
import database.Log import database.Log
@@ -21,6 +22,7 @@ import org.tinylog.provider.ProviderRegistry
import oshi.util.GlobalConfig import oshi.util.GlobalConfig
import web.WebApp import web.WebApp
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Paths
import kotlin.concurrent.fixedRateTimer import kotlin.concurrent.fixedRateTimer
import kotlin.io.path.absolutePathString import kotlin.io.path.absolutePathString
@@ -29,7 +31,7 @@ lateinit var audioPlayer: AudioPlayer
val StreamerOutputs: MutableMap<String, BarixConnection> = HashMap() val StreamerOutputs: MutableMap<String, BarixConnection> = HashMap()
lateinit var udpreceiver: UDPReceiver lateinit var udpreceiver: UDPReceiver
lateinit var tcpreceiver: TCPReceiver lateinit var tcpreceiver: TCPReceiver
const val version = "0.0.2 (23/09/2025)" const val version = "0.0.6 (14/10/2025)"
// AAS 64 channels // AAS 64 channels
const val max_channel = 64 const val max_channel = 64
@@ -52,11 +54,17 @@ val contentCache = ContentCache()
* Create necessary folders if not exist * Create necessary folders if not exist
*/ */
fun folder_preparation(){ fun folder_preparation(){
// sementara diset begini, nanti pake config file
Somecodes.Soundbank_directory = Paths.get("c:\\soundbank")
Files.createDirectories(Somecodes.SoundbankResult_directory) Files.createDirectories(Somecodes.SoundbankResult_directory)
Files.createDirectories(Somecodes.PagingResult_directory) Files.createDirectories(Somecodes.PagingResult_directory)
Files.createDirectories(Somecodes.Soundbank_directory) Files.createDirectories(Somecodes.Soundbank_directory)
Somecodes.Soundbank_Languages_directory.forEach { Language.entries.forEach { language ->
Files.createDirectories(it) VoiceType.entries.forEach { voice ->
Category.entries.forEach { category ->
Files.createDirectories(Somecodes.SoundbankDirectory(language, voice, category) )
}
}
} }
} }
@@ -176,7 +184,7 @@ fun main() {
StreamerOutputs[cmd.ipaddress] = _bc StreamerOutputs[cmd.ipaddress] = _bc
Logger.info { "Created new Streamer Output for channel ${_sc.channel} with IP ${cmd.ipaddress}" } 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 { } else {
// sudah ada, update data // sudah ada, update data

View File

@@ -588,11 +588,12 @@ class MainExtension01 {
Logger.info { logmessage } Logger.info { logmessage }
db.Add_Log("AAS", logmessage) db.Add_Log("AAS", logmessage)
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
return true return true
} else { } else {
// file tidak valid, delete from queue paging // file tidak valid, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
db.Add_Log( db.Add_Log(
"AAS", "AAS",
"Cancelled paging message with index ${qp.index} due to invalid audio file" "Cancelled paging message with index ${qp.index} due to invalid audio file"
@@ -602,11 +603,13 @@ class MainExtension01 {
} else { } else {
// ada broadcast zone yang tidak valid, delete from queue paging // ada broadcast zone yang tidak valid, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to invalid broadcast zone") db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to invalid broadcast zone")
} }
} else { } else {
// file tidak valid, delete from queue paging // file tidak valid, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
db.Add_Log( db.Add_Log(
"AAS", "AAS",
"Cancelled paging message with index ${qp.index} due to invalid audio file" "Cancelled paging message with index ${qp.index} due to invalid audio file"
@@ -615,6 +618,7 @@ class MainExtension01 {
} else { } else {
// invalid broadcast zone, delete from queue paging // invalid broadcast zone, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to empty broadcast zone") db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to empty broadcast zone")
} }
} }
@@ -688,6 +692,7 @@ class MainExtension01 {
Logger.info { logmsg } Logger.info { logmsg }
db.Add_Log("AAS", logmsg) db.Add_Log("AAS", logmsg)
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
return true return true
} else { } else {
db.Add_Log( db.Add_Log(
@@ -698,6 +703,7 @@ class MainExtension01 {
} }
} else{ } else{
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
db.Add_Log("AAS", result.message) db.Add_Log("AAS", result.message)
} }
} }
@@ -706,6 +712,7 @@ class MainExtension01 {
} else { } else {
// tidak ada messagebank dengan ann_id ini, delete from queue paging // tidak ada messagebank dengan ann_id ini, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
db.Add_Log( db.Add_Log(
"AAS", "AAS",
"Cancelled Shalat message with index ${qp.index} due to ANN_ID $ann_id not found in Messagebank" "Cancelled Shalat message with index ${qp.index} due to ANN_ID $ann_id not found in Messagebank"
@@ -716,16 +723,19 @@ class MainExtension01 {
} else { } else {
// invalid ann_id, delete from queue paging // invalid ann_id, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to invalid ANN_ID") db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to invalid ANN_ID")
} }
} else { } else {
// ada broadcast zone yang tidak valid, delete from queue paging // ada broadcast zone yang tidak valid, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to invalid broadcast zone") db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to invalid broadcast zone")
} }
} else { } else {
// invalid broadcast zone, delete from queue paging // invalid broadcast zone, delete from queue paging
db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.queuepagingDB.DeleteByIndex(qp.index.toInt())
db.queuepagingDB.Resort()
db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to empty broadcast zone") db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to empty broadcast zone")
} }
} }
@@ -789,14 +799,17 @@ class MainExtension01 {
Logger.info { logmsg } Logger.info { logmsg }
db.Add_Log("AAS", logmsg) db.Add_Log("AAS", logmsg)
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
return true return true
} }
} else{ } else{
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
db.Add_Log("AAS", result.message) db.Add_Log("AAS", result.message)
} }
} else { } else {
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
db.Add_Log("AAS", result.message) db.Add_Log("AAS", result.message)
} }
@@ -805,6 +818,7 @@ class MainExtension01 {
} else { } else {
// tidak ada messagebank dengan ann_id ini, delete from queue table // tidak ada messagebank dengan ann_id ini, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
db.Add_Log( db.Add_Log(
"AAS", "AAS",
"Cancelled TIMER with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank" "Cancelled TIMER with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank"
@@ -815,16 +829,19 @@ class MainExtension01 {
} else { } else {
// invalid ann_id, delete from queue table // invalid ann_id, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to invalid ANN_ID") db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to invalid ANN_ID")
} }
} else { } else {
// ada broadcast zone yang tidak valid, delete from queue table // ada broadcast zone yang tidak valid, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to invalid broadcast zone") db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to invalid broadcast zone")
} }
} else { } else {
// invalid broadcast zone, delete from queue table // invalid broadcast zone, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to empty broadcast zone") db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to empty broadcast zone")
} }
} }
@@ -903,6 +920,7 @@ class MainExtension01 {
"Cancelled SOUNDBANK message with index ${qa.index} due to missing or invalid ANN_ID in SB_TAGS" "Cancelled SOUNDBANK message with index ${qa.index} due to missing or invalid ANN_ID in SB_TAGS"
) )
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
return@forEach return@forEach
} }
// sampe sini punya ann_id valid // sampe sini punya ann_id valid
@@ -930,12 +948,14 @@ class MainExtension01 {
} else { } else {
db.Add_Log("AAS", result.message) db.Add_Log("AAS", result.message)
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
} }
} }
if (listafi.isNotEmpty()){ if (listafi.isNotEmpty()){
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
// bikin nama file ada postifx nya dari SB_TAGS, tapi spasi diganti underscore // bikin nama file ada postifx nya dari SB_TAGS, tapi spasi diganti underscore
// dan titik dua diganti dash // dan titik dua diganti dash
val postfix = qa.SB_TAGS.replace(" ","_").replace(":","-") val postfix = qa.SB_TAGS.replace(" ","_").replace(":","-")
@@ -972,6 +992,7 @@ class MainExtension01 {
} else { } else {
println("Failed to write WAV file: ${result.message}") println("Failed to write WAV file: ${result.message}")
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
db.Add_Log("AAS", result.message) db.Add_Log("AAS", result.message)
} }
@@ -979,6 +1000,7 @@ class MainExtension01 {
} else { } else {
// tidak ada messagebank dengan ann_id ini, delete from queue table // tidak ada messagebank dengan ann_id ini, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
db.Add_Log( db.Add_Log(
"AAS", "AAS",
"Cancelled SOUNDBANK message with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank" "Cancelled SOUNDBANK message with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank"
@@ -990,11 +1012,13 @@ class MainExtension01 {
} else { } else {
// ada broadcast zone yang tidak valid, delete from queue table // ada broadcast zone yang tidak valid, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
db.Add_Log("AAS", "Cancelled SOUNDBANK message with index ${qa.index} due to invalid broadcast zone") db.Add_Log("AAS", "Cancelled SOUNDBANK message with index ${qa.index} due to invalid broadcast zone")
} }
} else { } else {
// invalid broadcast zone, delete from queue table // invalid broadcast zone, delete from queue table
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
db.queuetableDB.Resort()
db.Add_Log("AAS", "Cancelled SOUNDBANK message with index ${qa.index} due to empty broadcast zone") db.Add_Log("AAS", "Cancelled SOUNDBANK message with index ${qa.index} due to empty broadcast zone")
} }
} }

View File

@@ -20,7 +20,6 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin
private var _sd: Int = 0 private var _sd: Int = 0
private var _vu: Int = 0 private var _vu: Int = 0
private var _onlinecounter = 0 private var _onlinecounter = 0
private val udp = DatagramSocket(0)
private val inet = InetSocketAddress(ipaddress, port) private val inet = InetSocketAddress(ipaddress, port)
private val maxUDPsize = 1000 private val maxUDPsize = 1000
private var _tcp: Socket? = null private var _tcp: Socket? = null
@@ -105,24 +104,26 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin
fun SendData(data: ByteArray, cbOK: Consumer<String>, cbFail: Consumer<String>) { fun SendData(data: ByteArray, cbOK: Consumer<String>, cbFail: Consumer<String>) {
if (data.isNotEmpty()) { if (data.isNotEmpty()) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
DatagramSocket().use{ udp ->
val bb = ByteBuffer.wrap(data) val bb = ByteBuffer.wrap(data)
while(bb.hasRemaining()){ while(bb.hasRemaining()){
try { try {
val chunk = ByteArray(if (bb.remaining() > maxUDPsize) maxUDPsize else bb.remaining()) val chunk = ByteArray(if (bb.remaining() > maxUDPsize) maxUDPsize else bb.remaining())
bb.get(chunk) bb.get(chunk)
while(bufferRemain<chunk.size){ //println("Buffer remain: $bufferRemain, sending chunk size: ${chunk.size}")
delay(10) while(bufferRemain<chunk.size){
delay(10)
//println("Waiting until buffer enough..")
}
udp.send(DatagramPacket(chunk, chunk.size, inet))
delay(2)
} catch (e: Exception) {
cbFail.accept("SendData to $ipaddress:$port failed, message: ${e.message}")
return@launch
} }
udp.send(DatagramPacket(chunk, chunk.size, inet))
delay(1)
} catch (e: Exception) {
cbFail.accept("SendData to $ipaddress:$port failed, message: ${e.message}")
return@launch
} }
cbOK.accept("SendData to $channel ($ipaddress:$port) succeeded, ${data.size} bytes sent")
} }
cbOK.accept("SendData to $channel ($ipaddress:$port) succeeded, ${data.size} bytes sent")
} }
} else cbFail.accept("SendData to $ipaddress:$port failed, data is empty") } else cbFail.accept("SendData to $ipaddress:$port failed, data is empty")

View File

@@ -3,8 +3,10 @@ package barix
import codes.Somecodes.Companion.ValidString import codes.Somecodes.Companion.ValidString
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.tinylog.Logger import org.tinylog.Logger
import java.io.DataInputStream
import java.net.ServerSocket import java.net.ServerSocket
import java.net.Socket import java.net.Socket
import java.nio.ByteBuffer
import java.util.function.Consumer import java.util.function.Consumer
@Suppress("unused") @Suppress("unused")
@@ -37,13 +39,19 @@ class TCP_Barix_Command_Server {
val key : String = socket.inetAddress.hostAddress val key : String = socket.inetAddress.hostAddress
socketMap[key] = socket socketMap[key] = socket
Logger.info { "Start communicating with Streamer Output with IP : $key" } Logger.info { "Start communicating with Streamer Output with IP : $key" }
val din = socket.getInputStream() try{
while (isActive) {
if (din.available()>0){ val din = DataInputStream(socket.getInputStream())
val bb = ByteArray(din.available()) while (isActive) {
din.read(bb) val length = ByteArray(4)
din.readFully(length)
val readlength = ByteBuffer.wrap(length).getInt()
//println("Read Length : $readlength")
val bb = ByteArray(readlength)
din.readFully(bb)
// B4A format, 4 bytes di depan adalah size // B4A format, 4 bytes di depan adalah size
val str = String(bb, 4, bb.size - 4) val str = String(bb)
//println("Received from $key : $str")
if (ValidString(str)) { if (ValidString(str)) {
// Valid command from Barix is in format $"STATUSBARIX;VU;BuffRemain;StatusData"$ // Valid command from Barix is in format $"STATUSBARIX;VU;BuffRemain;StatusData"$
pattern.find(str)?.let { matchResult -> pattern.find(str)?.let { matchResult ->
@@ -60,8 +68,12 @@ class TCP_Barix_Command_Server {
Logger.warn { "Invalid command format from $key : $str" } Logger.warn { "Invalid command format from $key : $str" }
} }
} }
} }
} catch (ex:Exception){
Logger.error { "Error in communication with Streamer Output with IP $key, Message : ${ex.message}" }
} }
Logger.info { "Finished communicating with Streamer Output with IP $key" } Logger.info { "Finished communicating with Streamer Output with IP $key" }
socketMap.remove(key) socketMap.remove(key)

View File

@@ -2,8 +2,10 @@ package codes
import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import content.Category
import content.Language import content.Language
import content.ScheduleDay import content.ScheduleDay
import content.VoiceType
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -25,15 +27,8 @@ import kotlin.io.path.name
class Somecodes { class Somecodes {
companion object { companion object {
val current_directory : String = System.getProperty("user.dir") val current_directory : String = System.getProperty("user.dir")
val Soundbank_directory : Path = Path.of(current_directory,"Soundbank") var Soundbank_directory : Path = Path.of(current_directory,"Soundbank")
val Soundbank_Languages_directory : List<Path> = listOf(
Soundbank_directory.resolve(Language.INDONESIA.name),
Soundbank_directory.resolve(Language.LOCAL.name),
Soundbank_directory.resolve(Language.ENGLISH.name),
Soundbank_directory.resolve(Language.CHINESE.name),
Soundbank_directory.resolve(Language.JAPANESE.name),
Soundbank_directory.resolve(Language.ARABIC.name)
)
val SoundbankResult_directory : Path = Path.of(current_directory,"SoundbankResult") val SoundbankResult_directory : Path = Path.of(current_directory,"SoundbankResult")
val PagingResult_directory : Path = Path.of(current_directory,"PagingResult") val PagingResult_directory : Path = Path.of(current_directory,"PagingResult")
@@ -56,6 +51,25 @@ class Somecodes {
// regex for getting ann_id from Message, which is the number inside [] // regex for getting ann_id from Message, which is the number inside []
private val ann_id_regex = Regex("\\[(\\d+)]") private val ann_id_regex = Regex("\\[(\\d+)]")
/**
* Get the directory path for a specific language, voice type, and category.
* @param language The language.
* @param voice The voice type.
* @param category The category.
* @return The path to the directory.
*/
fun SoundbankDirectory(language: Language, voice: VoiceType, category: Category) : Path{
return Soundbank_directory.resolve(language.name).resolve(voice.name).resolve(category.name)
}
fun SoundbankDirectory(language: String, voice: String, category: String) : Path{
return SoundbankDirectory(
Language.valueOf(language),
VoiceType.valueOf(voice),
Category.valueOf(category)
)
}
fun ExtractFilesFromClassPath(resourcePath: String, outputDir: Path) { fun ExtractFilesFromClassPath(resourcePath: String, outputDir: Path) {
try { try {
val resource = Somecodes::class.java.getResource(resourcePath) val resource = Somecodes::class.java.getResource(resourcePath)
@@ -157,14 +171,13 @@ class Somecodes {
/** /**
* List all audio files (.mp3 and .wav) in the specified directory and its subdirectories. * List all audio files (.mp3 and .wav) in the specified directory and its subdirectories.
* Only files larger than 1KB are included. * Only files larger than 1KB are included.
* @param dir The directory to search in (default is the current working directory). * @param p The directory Path to search in
* @return A list of absolute paths to the audio files found. * @return A list of absolute paths to the audio files found.
*/ */
fun ListAudioFiles(dir: String = current_directory) : List<String>{ fun ListAudioFiles(p: Path) : List<String>{
return try{ return try{
// find all files that ends with .mp3 or .wav // find all files that ends with .mp3 or .wav
// and find them recursively // and find them recursively
val p = Path.of(dir)
if (Files.exists(p) && Files.isDirectory(p)){ if (Files.exists(p) && Files.isDirectory(p)){
Files.walk(p) Files.walk(p)
// cari file regular saja // cari file regular saja
@@ -286,6 +299,23 @@ class Somecodes {
return value is String && value.isNotBlank() return value is String && value.isNotBlank()
} }
/**
* Check if all strings in a list are valid non-blank strings.
* @param values The list of strings to check.
* @return True if all strings in the list are valid non-blank strings, false otherwise.
*/
fun ValidStrings(values: List<String>) : Boolean{
if (values.isNotEmpty()){
for (v in values){
if (!ValidString(v)){
return false
}
}
return true
}
return false
}
/** /**
* Check if a string is a valid file path and the file exists. * Check if a string is a valid file path and the file exists.
* @param value The string to check. * @param value The string to check.

View File

@@ -220,6 +220,7 @@ class TCP_Android_Command_Server {
pj.broadcastzones pj.broadcastzones
) )
if (db.queuepagingDB.Add(qp)) { if (db.queuepagingDB.Add(qp)) {
db.queuepagingDB.Resort()
logcb.accept("Paging audio inserted to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}") logcb.accept("Paging audio inserted to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}")
cb.accept("PCMFILE_STOP;OK@") cb.accept("PCMFILE_STOP;OK@")
return return
@@ -274,6 +275,7 @@ class TCP_Android_Command_Server {
pj.broadcastzones pj.broadcastzones
) )
if (db.queuepagingDB.Add(qp)){ if (db.queuepagingDB.Add(qp)){
db.queuepagingDB.Resort()
logcb.accept("Paging audio inserted to queue paging table from IPM $key, file ${pj.filePath.absolutePathString()}") logcb.accept("Paging audio inserted to queue paging table from IPM $key, file ${pj.filePath.absolutePathString()}")
cb.accept("STOPPAGINGAND;OK@") cb.accept("STOPPAGINGAND;OK@")
return return
@@ -627,6 +629,7 @@ class TCP_Android_Command_Server {
lang lang
) )
if (db.queuetableDB.Add(qt)){ if (db.queuetableDB.Add(qt)){
db.queuetableDB.Resort()
logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} inserted. Message: $desc;$lang;$tags;$zone") 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@") cb.accept("BROADCASTAND;OK@")
return return

View File

@@ -12,7 +12,6 @@ import org.tinylog.Logger
import java.sql.Connection import java.sql.Connection
import java.sql.DriverManager import java.sql.DriverManager
import java.util.function.Consumer import java.util.function.Consumer
import kotlin.math.max
/** /**
* A class to manage a connection to a MariaDB database. * A class to manage a connection to a MariaDB database.
@@ -105,7 +104,7 @@ class MariaDB(
List.clear() List.clear()
try { try {
val statement = connection.createStatement() val statement = connection.createStatement()
val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName} ORDER BY Category, Language, VoiceType, TAG")
while (resultSet?.next() == true) { while (resultSet?.next() == true) {
val soundbank = Soundbank( val soundbank = Soundbank(
resultSet.getLong("index").toUInt(), resultSet.getLong("index").toUInt(),
@@ -221,13 +220,15 @@ class MariaDB(
override fun Resort(): Boolean { override fun Resort(): Boolean {
try { try {
val statement = connection.createStatement() val statement = connection.createStatement()
val tempdb_name = "temp_${super.dbName}"
// use a temporary table to reorder the index // use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM ${super.dbName} ORDER BY Description ") statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
statement?.executeUpdate("INSERT INTO $tempdb_name (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM ${super.dbName} ORDER BY Category, Language, VoiceType, TAG ")
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM temp_${super.dbName}") statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM $tempdb_name")
statement?.executeUpdate("DROP TABLE temp_${super.dbName}") statement?.executeUpdate("DROP TABLE $tempdb_name")
Logger.info("${super.dbName} table resorted by Description" as Any) Logger.info("${super.dbName} table resorted by Description" as Any)
// reload the local list // reload the local list
Get() Get()
@@ -421,12 +422,14 @@ class MariaDB(
override fun Resort(): Boolean { override fun Resort(): Boolean {
try { try {
val statement = connection.createStatement() val statement = connection.createStatement()
val tempdb_name = "temp_${super.dbName}"
// use a temporary table to reorder the index // use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM ${super.dbName} ORDER BY ANN_ID ") statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
statement?.executeUpdate("INSERT INTO $tempdb_name (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM ${super.dbName} ORDER BY ANN_ID ")
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM temp_${super.dbName}") statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM $tempdb_name")
statement?.executeUpdate("DROP TABLE temp_${super.dbName}") statement?.executeUpdate("DROP TABLE $tempdb_name")
Logger.info("${super.dbName} table resorted by Description" as Any) Logger.info("${super.dbName} table resorted by Description" as Any)
// reload the local list // reload the local list
Get() Get()
@@ -616,12 +619,14 @@ class MariaDB(
override fun Resort(): Boolean { override fun Resort(): Boolean {
try { try {
val statement = connection.createStatement() val statement = connection.createStatement()
val tempdb_name = "temp_${super.dbName}"
// use a temporary table to reorder the index // use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (TAG, Language) SELECT TAG, Language FROM ${super.dbName} ORDER BY TAG") statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
statement?.executeUpdate("INSERT INTO $tempdb_name (TAG, Language) SELECT TAG, Language FROM ${super.dbName} ORDER BY TAG")
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
statement?.executeUpdate("INSERT INTO ${super.dbName} (TAG, Language) SELECT TAG, Language FROM temp_${super.dbName}") statement?.executeUpdate("INSERT INTO ${super.dbName} (TAG, Language) SELECT TAG, Language FROM $tempdb_name")
statement?.executeUpdate("DROP TABLE temp_${super.dbName}") statement?.executeUpdate("DROP TABLE $tempdb_name")
Logger.info("${super.dbName} table resorted by TAG" as Any) Logger.info("${super.dbName} table resorted by TAG" as Any)
// reload the local list // reload the local list
Get() Get()
@@ -834,12 +839,14 @@ class MariaDB(
override fun Resort(): Boolean { override fun Resort(): Boolean {
try { try {
val statement = connection.createStatement() val statement = connection.createStatement()
val tempdb_name = "temp_${super.dbName}"
// use a temporary table to reorder the index // use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM ${super.dbName} ORDER BY Day , Time ") statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
statement?.executeUpdate("INSERT INTO $tempdb_name (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM ${super.dbName} ORDER BY Day , Time ")
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM temp_${super.dbName}") statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM $tempdb_name")
statement?.executeUpdate("DROP TABLE temp_${super.dbName}") statement?.executeUpdate("DROP TABLE $tempdb_name")
Logger.info("${super.dbName} table resorted by Day and Time" as Any) Logger.info("${super.dbName} table resorted by Day and Time" as Any)
// reload the local list // reload the local list
Get() Get()
@@ -988,7 +995,7 @@ class MariaDB(
override fun Add(data: BroadcastZones): Boolean { override fun Add(data: BroadcastZones): Boolean {
try { try {
val statement = val statement =
connection.prepareStatement("INSERT INTO ${super.dbName} (description, SoundChannel, Box, Relay) VALUES (?, ?, ?, ?)") connection.prepareStatement("INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) VALUES (?, ?, ?, ?)")
statement?.setString(1, data.description) statement?.setString(1, data.description)
statement?.setString(2, data.SoundChannel) statement?.setString(2, data.SoundChannel)
statement?.setString(3, data.id) statement?.setString(3, data.id)
@@ -1010,7 +1017,7 @@ class MariaDB(
try { try {
connection.autoCommit = false connection.autoCommit = false
val sql = val sql =
"INSERT INTO ${super.dbName} (description, SoundChannel, Box, Relay) VALUES (?, ?, ?, ?)" "INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) VALUES (?, ?, ?, ?)"
val statement = connection.prepareStatement(sql) val statement = connection.prepareStatement(sql)
for (bz in data) { for (bz in data) {
statement.setString(1, bz.description) statement.setString(1, bz.description)
@@ -1033,7 +1040,7 @@ class MariaDB(
override fun UpdateByIndex(index: Int, data: BroadcastZones): Boolean { override fun UpdateByIndex(index: Int, data: BroadcastZones): Boolean {
try { try {
val statement = val statement =
connection.prepareStatement("UPDATE ${super.dbName} SET description = ?, SoundChannel = ?, Box = ?, Relay = ? WHERE `index` = ?") connection.prepareStatement("UPDATE ${super.dbName} SET description = ?, SoundChannel = ?, id = ?, bp = ? WHERE `index` = ?")
statement?.setString(1, data.description) statement?.setString(1, data.description)
statement?.setString(2, data.SoundChannel) statement?.setString(2, data.SoundChannel)
statement?.setString(3, data.id) statement?.setString(3, data.id)
@@ -1055,12 +1062,14 @@ class MariaDB(
override fun Resort(): Boolean { override fun Resort(): Boolean {
try { try {
val statement = connection.createStatement() val statement = connection.createStatement()
val tempdb_name = "temp_${super.dbName}"
// use a temporary table to reorder the index // use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (description, SoundChannel, Box, Relay) SELECT description, SoundChannel, Box, Relay FROM ${super.dbName} ORDER BY description ") statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
statement?.executeUpdate("INSERT INTO $tempdb_name (description, SoundChannel, id, bp) SELECT description, SoundChannel, id, bp FROM ${super.dbName} ORDER BY description ")
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
statement?.executeUpdate("INSERT INTO ${super.dbName} (description, SoundChannel, Box, Relay) SELECT description, SoundChannel, Box, Relay FROM temp_${super.dbName}") statement?.executeUpdate("INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) SELECT description, SoundChannel, id, bp FROM $tempdb_name")
statement?.executeUpdate("DROP TABLE temp_${super.dbName}") statement?.executeUpdate("DROP TABLE $tempdb_name")
Logger.info("${super.dbName} table resorted by description" as Any) Logger.info("${super.dbName} table resorted by description" as Any)
// reload the local list // reload the local list
Get() Get()
@@ -1076,7 +1085,7 @@ class MariaDB(
val sheet = workbook.getSheet("BroadcastZones") val sheet = workbook.getSheet("BroadcastZones")
?: throw Exception("No sheet named 'BroadcastZones' found") ?: throw Exception("No sheet named 'BroadcastZones' found")
val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") val headerRow = sheet.getRow(0) ?: throw Exception("No header row found")
val headers = arrayOf("Index", "description", "SoundChannel", "Box", "Relay") val headers = arrayOf("Index", "description", "SoundChannel", "id", "bp")
for ((colIndex, header) in headers.withIndex()) { for ((colIndex, header) in headers.withIndex()) {
val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found")
if (cell.stringCellValue != header) throw Exception("Header '$header' not found") if (cell.stringCellValue != header) throw Exception("Header '$header' not found")
@@ -1089,9 +1098,9 @@ class MariaDB(
val row = sheet.getRow(rowIndex) ?: continue val row = sheet.getRow(rowIndex) ?: continue
val description = row.getCell(1)?.stringCellValue ?: continue val description = row.getCell(1)?.stringCellValue ?: continue
val soundChannel = row.getCell(2)?.stringCellValue ?: continue val soundChannel = row.getCell(2)?.stringCellValue ?: continue
val box = row.getCell(3)?.stringCellValue ?: continue val id = row.getCell(3)?.stringCellValue ?: continue
val relay = row.getCell(4)?.stringCellValue ?: continue val bp = row.getCell(4)?.stringCellValue ?: continue
val broadcastZone = BroadcastZones(0u, description, soundChannel, box, relay) val broadcastZone = BroadcastZones(0u, description, soundChannel, id, bp)
_broadcastZonesList.add(broadcastZone) _broadcastZonesList.add(broadcastZone)
} }
return AddAll(_broadcastZonesList) return AddAll(_broadcastZonesList)
@@ -1108,7 +1117,7 @@ class MariaDB(
val workbook = XSSFWorkbook() val workbook = XSSFWorkbook()
val sheet = workbook.createSheet("BroadcastZones") val sheet = workbook.createSheet("BroadcastZones")
val headerRow = sheet.createRow(0) val headerRow = sheet.createRow(0)
val headers = arrayOf("Index", "description", "SoundChannel", "Box", "Relay") val headers = arrayOf("Index", "description", "SoundChannel", "id", "bp")
for ((colIndex, header) in headers.withIndex()) { for ((colIndex, header) in headers.withIndex()) {
val cell = headerRow.createCell(colIndex) val cell = headerRow.createCell(colIndex)
cell.setCellValue(header) cell.setCellValue(header)
@@ -1119,8 +1128,8 @@ class MariaDB(
row.createCell(0).setCellValue(resultSet.getString("index")) row.createCell(0).setCellValue(resultSet.getString("index"))
row.createCell(1).setCellValue(resultSet.getString("description")) row.createCell(1).setCellValue(resultSet.getString("description"))
row.createCell(2).setCellValue(resultSet.getString("SoundChannel")) row.createCell(2).setCellValue(resultSet.getString("SoundChannel"))
row.createCell(3).setCellValue(resultSet.getString("Box")) row.createCell(3).setCellValue(resultSet.getString("id"))
row.createCell(4).setCellValue(resultSet.getString("Relay")) row.createCell(4).setCellValue(resultSet.getString("bp"))
} }
for (i in headers.indices) { for (i in headers.indices) {
sheet.autoSizeColumn(i) sheet.autoSizeColumn(i)
@@ -1237,12 +1246,14 @@ class MariaDB(
override fun Resort(): Boolean { override fun Resort(): Boolean {
try { try {
val statement = connection.createStatement() val statement = connection.createStatement()
val tempdb_name = "temp_${super.dbName}"
// use a temporary table to reorder the index // use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, Repeat, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, Repeat, Language FROM ${super.dbName} ORDER BY `index` ") statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
statement?.executeUpdate("INSERT INTO $tempdb_name (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language FROM ${super.dbName} ORDER BY `index` ")
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, Repeat, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, Repeat, Language FROM temp_${super.dbName}") statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language FROM $tempdb_name")
statement?.executeUpdate("DROP TABLE temp_${super.dbName}") statement?.executeUpdate("DROP TABLE $tempdb_name")
Logger.info("${super.dbName} table resorted by index" as Any) Logger.info("${super.dbName} table resorted by index" as Any)
// reload the local list // reload the local list
Get() Get()
@@ -1396,12 +1407,14 @@ class MariaDB(
override fun Resort(): Boolean { override fun Resort(): Boolean {
try { try {
val statement = connection.createStatement() val statement = connection.createStatement()
val tempdb_name = "temp_${super.dbName}"
// use a temporary table to reorder the index // use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS) SELECT Date_Time, Source, Type, Message, SB_TAGS FROM ${super.dbName} ORDER BY `index` ") statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
statement?.executeUpdate("INSERT INTO $tempdb_name (Date_Time, Source, Type, Message, SB_TAGS) SELECT Date_Time, Source, Type, Message, SB_TAGS FROM ${super.dbName} ORDER BY `index` ")
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS) SELECT Date_Time, Source, Type, Message, SB_TAGS FROM temp_${super.dbName}") statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS) SELECT Date_Time, Source, Type, Message, SB_TAGS FROM $tempdb_name")
statement?.executeUpdate("DROP TABLE temp_${super.dbName}") statement?.executeUpdate("DROP TABLE $tempdb_name")
Logger.info("${super.dbName} table resorted by index" as Any) Logger.info("${super.dbName} table resorted by index" as Any)
// reload the local list // reload the local list
Get() Get()
@@ -1557,12 +1570,14 @@ class MariaDB(
override fun Resort(): Boolean { override fun Resort(): Boolean {
try { try {
val statement = connection.createStatement() val statement = connection.createStatement()
val tempdb_name = "temp_${super.dbName}"
// use a temporary table to reorder the index // use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (channel, ip) SELECT channel, ip FROM ${super.dbName} ORDER BY `index` ") statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
statement?.executeUpdate("INSERT INTO $tempdb_name (channel, ip) SELECT channel, ip FROM ${super.dbName} ORDER BY `index` ")
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
statement?.executeUpdate("INSERT INTO ${super.dbName} (channel, ip) SELECT channel, ip FROM temp_${super.dbName}") statement?.executeUpdate("INSERT INTO ${super.dbName} (channel, ip) SELECT channel, ip FROM $tempdb_name")
statement?.executeUpdate("DROP TABLE temp_${super.dbName}") statement?.executeUpdate("DROP TABLE $tempdb_name")
Logger.info("${super.dbName} table resorted by index" as Any) Logger.info("${super.dbName} table resorted by index" as Any)
// reload the local list // reload the local list
Get() Get()
@@ -1770,19 +1785,21 @@ class MariaDB(
override fun Resort(): Boolean { override fun Resort(): Boolean {
try { try {
val statement = connection.createStatement() val statement = connection.createStatement()
val tempdb_name = "temp_${super.dbName}"
// use a temporary table to reorder the index // use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
statement?.executeUpdate( statement?.executeUpdate(
"INSERT INTO temp_${super.dbName} (datenya, timenya, machine, description) " + "INSERT INTO $tempdb_name (datenya, timenya, machine, description) " +
"SELECT datenya, timenya, machine, description FROM ${super.dbName} " + "SELECT datenya, timenya, machine, description FROM ${super.dbName} " +
"ORDER BY datenya , timenya , machine " "ORDER BY datenya , timenya , machine "
) )
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
statement?.executeUpdate( statement?.executeUpdate(
"INSERT INTO ${super.dbName} (datenya, timenya, machine, description) " + "INSERT INTO ${super.dbName} (datenya, timenya, machine, description) " +
"SELECT datenya, timenya, machine, description FROM temp_${super.dbName}" "SELECT datenya, timenya, machine, description FROM $tempdb_name"
) )
statement?.executeUpdate("DROP TABLE temp_${super.dbName}") statement?.executeUpdate("DROP TABLE $tempdb_name")
Logger.info("${super.dbName} table resorted by datenya, timenya, machine" as Any) Logger.info("${super.dbName} table resorted by datenya, timenya, machine" as Any)
// reload the local list // reload the local list
Get() Get()
@@ -1947,12 +1964,14 @@ class MariaDB(
override fun Resort(): Boolean { override fun Resort(): Boolean {
try { try {
val statement = connection.createStatement() val statement = connection.createStatement()
val tempdb_name = "temp_${super.dbName}"
// use a temporary table to reorder the index // use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
statement?.executeUpdate("INSERT INTO temp_${super.dbName} (username, password, location, soundbank_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, soundbank_tags, messagebank_ann_id, broadcastzones FROM ${super.dbName} ORDER BY username ") statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
statement?.executeUpdate("INSERT INTO $tempdb_name (username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones FROM ${super.dbName} ORDER BY username ")
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
statement?.executeUpdate("INSERT INTO ${super.dbName} (username, password, location, soundbank_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, soundbank_tags, messagebank_ann_id, broadcastzones FROM temp_${super.dbName}") statement?.executeUpdate("INSERT INTO ${super.dbName} (username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones FROM $tempdb_name")
statement?.executeUpdate("DROP TABLE temp_${super.dbName}") statement?.executeUpdate("DROP TABLE $tempdb_name")
Logger.info("${super.dbName} table resorted by index" as Any) Logger.info("${super.dbName} table resorted by index" as Any)
// reload the local list // reload the local list
Get() Get()
@@ -2254,24 +2273,21 @@ class MariaDB(
* @return a list of Soundbank with Category City and matching tag * @return a list of Soundbank with Category City and matching tag
*/ */
fun Find_Soundbank_City(tag: String) : List<Soundbank>{ fun Find_Soundbank_City(tag: String) : List<Soundbank>{
val lowerTag = tag.lowercase()
return soundDB.List return soundDB.List
.filter{ it.Category== Category.City.name } .filter{ it.Category== Category.City.name }
.filter { it.TAG.lowercase()==lowerTag} .filter { it.TAG.equals(tag,true)}
} }
fun Find_Soundbank_AirplaneName(tag: String) : List<Soundbank>{ fun Find_Soundbank_AirplaneName(tag: String) : List<Soundbank>{
val lowerTag = tag.lowercase()
return soundDB.List return soundDB.List
.filter{ it.Category== Category.Airplane_Name.name } .filter{ it.Category== Category.Airplane_Name.name }
.filter { it.TAG.lowercase()==lowerTag} .filter { it.TAG.equals(tag,true)}
} }
fun Find_Soundbank_Places(tag: String) : List<Soundbank>{ fun Find_Soundbank_Places(tag: String) : List<Soundbank>{
val lowerTag = tag.lowercase()
return soundDB.List return soundDB.List
.filter{ it.Category== Category.Places.name } .filter{ it.Category== Category.Places.name }
.filter { it.TAG.lowercase()==lowerTag} .filter { it.TAG.equals(tag,true)}
} }
fun Find_Soundbank_Shalat(tag: String) : List<Soundbank>{ fun Find_Soundbank_Shalat(tag: String) : List<Soundbank>{
@@ -2282,25 +2298,85 @@ class MariaDB(
} }
fun Find_Soundbank_Sequence(tag: String) : List<Soundbank>{ fun Find_Soundbank_Sequence(tag: String) : List<Soundbank>{
val lowerTag = tag.lowercase()
return soundDB.List return soundDB.List
.filter{ it.Category== Category.Sequence.name } .filter{ it.Category== Category.Sequence.name }
.filter { it.TAG.lowercase()==lowerTag} .filter { it.TAG.equals(tag,true)}
} }
fun Find_Soundbank_Reason(tag: String) : List<Soundbank>{ fun Find_Soundbank_Reason(tag: String) : List<Soundbank>{
val lowerTag = tag.lowercase()
return soundDB.List return soundDB.List
.filter{ it.Category== Category.Reason.name } .filter{ it.Category== Category.Reason.name }
.filter { it.TAG.lowercase()==lowerTag} .filter { it.TAG.equals(tag,true)}
} }
fun Find_Soundbank_Procedure(tag: String) : List<Soundbank> { fun Find_Soundbank_Procedure(tag: String) : List<Soundbank> {
val lowerTag = tag.lowercase()
return soundDB.List return soundDB.List
.filter { it.Category == Category.Procedure.name } .filter { it.Category == Category.Procedure.name }
.filter { it.TAG.lowercase() == lowerTag } .filter { it.TAG.equals(tag,true) }
}
/**
* Get all distinct airline code tags from soundbank
* @return a list of distinct airline code tags sorted alphabetically
*/
fun Get_AirlineCode_Tags(): List<String> {
return soundDB.List
.filter { it.Category == Category.Airline_Code.name }
.map { it.TAG }
.distinct()
.sorted()
}
/**
* Get all distinct city tags from soundbank
* @return a list of distinct city tags sorted alphabetically
*/
fun Get_City_Tags(): List<String> {
return soundDB.List
.filter { it.Category == Category.City.name }
.map { it.TAG }
.distinct()
.sorted()
}
/**
* Get all distinct message ID from messagebank
* @return a list of distinct ANN_ID sorted numerically
*/
fun Get_MessageID_List(): List<UInt> {
return messageDB.List
.distinctBy { it.ANN_ID }
.map { it.ANN_ID }
.sorted()
}
/**
* Get all distinct broadcast zone descriptions from broadcastDB
* @return a list of distinct broadcast zone descriptions sorted alphabetically
*/
fun Get_BroadcastZone_List(): List<String> {
return broadcastDB.List
.distinctBy { it.description }
.map { it.description }
.sorted()
}
/**
* Get all distinct sound channel from soundchannelDB
* @return a list of distinct sound channel sorted alphabetically
*/
fun Get_SoundChannel_List(): List<String> {
return soundchannelDB.List
.distinctBy { it.channel }
.map { it.channel }
.sorted()
}
/**
* Check if a username already exists in the userDB (case-insensitive)
*/
fun Username_exists(username: String): Boolean {
return userDB.List.any { it.username.equals(username, ignoreCase = true) }
} }

View File

@@ -1,5 +1,7 @@
package database package database
import content.Category
data class Soundbank( data class Soundbank(
var index: UInt, var index: UInt,
var Description: String, var Description: String,
@@ -8,4 +10,45 @@ data class Soundbank(
var Language: String, var Language: String,
var VoiceType: String, var VoiceType: String,
var Path: String, var Path: String,
) ){
/**
* Check if all fields are not empty
*/
fun isNotEmpty(): Boolean{
if (Description.isNotEmpty()){
if (TAG.isNotEmpty()){
if (Category.isNotEmpty()){
if (Language.isNotEmpty()){
if (VoiceType.isNotEmpty()){
if (Path.isNotEmpty()){
return true
}
}
}
}
}
}
return false
}
/**
* Check if Category is valid
*/
fun ValidCategory() : Boolean{
return content.Category.entries.any{ it.name == this.Category}
}
/**
* Check if VoiceType is valid
*/
fun ValidVoiceType() : Boolean{
return content.VoiceType.entries.any{ it.name == this.VoiceType}
}
/**
* Check if Language is valid
*/
fun ValidLanguage() : Boolean{
return content.Language.entries.any{ it.name == this.Language}
}
}

View File

@@ -5,6 +5,22 @@ data class UserDB(var index: UInt, var username: String, var password: String, v
override fun toString(): String { override fun toString(): String {
return "UserDB(index=$index, username='$username', location='$location', airline_tags='$airline_tags', city_tags='$city_tags', messagebank_ann_id='$messagebank_ann_id', broadcastzones='$broadcastzones')" return "UserDB(index=$index, username='$username', location='$location', airline_tags='$airline_tags', city_tags='$city_tags', messagebank_ann_id='$messagebank_ann_id', broadcastzones='$broadcastzones')"
} }
/**
* Compares this UserDB object with another UserDB object for equality.
* Two UserDB objects are considered equal if all their properties are equal, except for the index property.
*/
fun isEqual(other: UserDB?): Boolean {
if (other == null) return false
return this.username == other.username &&
this.password == other.password &&
this.location == other.location &&
this.airline_tags == other.airline_tags &&
this.city_tags == other.city_tags &&
this.messagebank_ann_id == other.messagebank_ann_id &&
this.broadcastzones == other.broadcastzones
}
} }

View File

@@ -0,0 +1,4 @@
package web
class KeyValueMessage(val key: String, val value: String) {
}

View File

@@ -15,7 +15,15 @@ import content.Category
import content.Language import content.Language
import content.ScheduleDay import content.ScheduleDay
import content.VoiceType import content.VoiceType
import database.* import database.BroadcastZones
import database.LanguageLink
import database.MariaDB
import database.Messagebank
import database.ScheduleBank
import database.SoundChannel
import database.Soundbank
import database.UserDB
//>>>>>>>>> Temporary merge branch 2
import db import db
import io.javalin.Javalin import io.javalin.Javalin
import io.javalin.apibuilder.ApiBuilder.before import io.javalin.apibuilder.ApiBuilder.before
@@ -29,8 +37,10 @@ import io.javalin.http.Context
import io.javalin.json.JavalinJackson import io.javalin.json.JavalinJackson
import io.javalin.websocket.WsMessageContext import io.javalin.websocket.WsMessageContext
import org.apache.poi.xssf.usermodel.XSSFWorkbook import org.apache.poi.xssf.usermodel.XSSFWorkbook
import java.nio.file.Files
import java.time.LocalDateTime import java.time.LocalDateTime
@Suppress("unused") @Suppress("unused")
class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) { class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
@@ -192,43 +202,81 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
it.result(objectmapper.writeValueAsString(ScheduleDay.entries.map { day -> day.toString() })) it.result(objectmapper.writeValueAsString(ScheduleDay.entries.map { day -> day.toString() }))
} }
} }
path("ListFiles/{Language}/{VoiceType}/{Category}") {
get { ctx ->
val language = ctx.pathParam("Language")
val voiceType = ctx.pathParam("VoiceType")
val category = ctx.pathParam("Category")
if (ValidString(language) && Language.entries.any { lang -> lang.name == language }) {
if (ValidString(voiceType) && VoiceType.entries.any { vt -> vt.name == voiceType }) {
if (ValidString(category) && Category.entries.any { cat -> cat.name == category }) {
val dir = Somecodes.SoundbankDirectory(
Language.valueOf(language),
VoiceType.valueOf(voiceType),
Category.valueOf(category)
)
if (Files.isDirectory(dir)){
val list = ListAudioFiles(dir)
ctx.result(objectmapper.writeValueAsString(list))
} else ctx.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Directory does not exist")))
} else ctx.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Invalid Category")))
} else ctx.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Invalid VoiceType")))
} else ctx.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Invalid Language")))
}
}
path("SoundBank") { path("SoundBank") {
get("List") { get("List") {
it.result(MariaDB.ArrayListtoString(db.soundDB.List)) it.result(MariaDB.ArrayListtoString(db.soundDB.List))
} }
get("ListFiles"){ get("ListFiles") {
it.result(objectmapper.writeValueAsString(ListAudioFiles("C:\\soundbank"))) it.result(objectmapper.writeValueAsString(ListAudioFiles(Somecodes.Soundbank_directory)))
}
get("AirlineTags") { ctx ->
val value = db.soundDB.List
.filter { it.Category == Category.Airplane_Name.name }
.distinctBy { it.TAG }
.sortedBy { it.TAG }
.map { KeyValueMessage(it.TAG, it.Description) }
ctx.result(objectmapper.writeValueAsString(value))
}
get("CityTags") { ctx ->
val value = db.soundDB.List
.filter { it.Category == Category.City.name }
.distinctBy { it.TAG }
.sortedBy { it.TAG }
.map { KeyValueMessage(it.TAG, it.Description) }
ctx.result(objectmapper.writeValueAsString(value))
} }
post("Add") { post("Add") {
try { try {
val addvalue = objectmapper.readValue(it.body(), Soundbank::class.java) val addvalue = objectmapper.readValue(it.body(), Soundbank::class.java)
if (ValidString(addvalue.Description)) { if (addvalue.isNotEmpty()){
if (ValidString(addvalue.TAG)) { // check apakah TAG sudah ada untuk language, category dan voicetype yang sama
if (ValidString(addvalue.Category)) { val exists = db.soundDB.List.any { sb ->
if (ValidString(addvalue.Language)) { sb.TAG == addvalue.TAG && sb.Language == addvalue.Language && sb.Category == addvalue.Category && sb.VoiceType == addvalue.VoiceType
if (ValidString(addvalue.Path)) { }
// check apakah TAG sudah ada untuk language dan category yang sama if (!exists) {
val exists = db.soundDB.List.any { sb -> val absolutepath = Somecodes.SoundbankDirectory(Language.valueOf(addvalue.Language), VoiceType.valueOf(addvalue.VoiceType), Category.valueOf(addvalue.Category)).resolve(addvalue.Path)
sb.TAG == addvalue.TAG && sb.Language == addvalue.Language && sb.Category == addvalue.Category if (Files.isRegularFile(absolutepath)) {
} addvalue.Path = absolutepath.toAbsolutePath().toString()
if (!exists) { if (db.soundDB.Add(addvalue)) {
if (ValidFile(addvalue.Path)) { db.soundDB.Resort()
if (db.soundDB.Add(addvalue)) { it.result(objectmapper.writeValueAsString(resultMessage("OK")))
db.soundDB.Resort() } else it.status(500)
it.result(objectmapper.writeValueAsString(resultMessage("OK"))) .result(objectmapper.writeValueAsString(resultMessage("Failed to add soundbank to database")))
} else it.status(500) } else it.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Failed to add soundbank to database"))) .result(objectmapper.writeValueAsString(resultMessage("Invalid Path, file does not exist")))
} else it.status(400) } else it.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Invalid Path, file does not exist"))) .result(objectmapper.writeValueAsString(resultMessage("TAG=${addvalue.TAG} already exists for the same Language=${addvalue.Language} and Category=${addvalue.Category}")))
} else it.status(400) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Some fields are empty")))
.result(objectmapper.writeValueAsString(resultMessage("TAG=${addvalue.TAG} already exists for the same Language=${addvalue.Language} and Category=${addvalue.Category}")))
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Path"))) } catch (e: Exception) {
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Language"))) it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid request body, Message: ${e.message}")))
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Category")))
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid TAG")))
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Description")))
} catch (_: Exception) {
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid request body")))
} }
} }
delete("List") { delete("List") {
@@ -257,68 +305,78 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
patch("UpdateByIndex/{index}") { patch("UpdateByIndex/{index}") {
// update by index // update by index
val index = it.pathParam("index").toUIntOrNull() val index = it.pathParam("index").toUIntOrNull()
if (index == null) { if (index!=null){
// tidak ada path param index
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index")))
} else {
val sb = db.soundDB.List.find { xx -> xx.index == index } val sb = db.soundDB.List.find { xx -> xx.index == index }
if (sb == null) { if (sb!=null){
// soundbank dengan index tersebut tidak ditemukan try{
it.status(404).result(objectmapper.writeValueAsString(resultMessage("Soundbank with index $index not found"))) //println("index=$index, body=${it.body()}")
} else { val newsb = objectmapper.readValue(it.body(), Soundbank::class.java)
// soundbank dengan index tersebut ditemukan, sekarang update //println("newsb=$newsb")
val json: JsonNode = objectmapper.readTree(it.body()) if (newsb.isNotEmpty()){
if (json.isEmpty) { var varchanged = false
it.status(400).result(objectmapper.writeValueAsString(resultMessage("UpdateByIndex with index=$index has empty body"))) if (newsb.Description != sb.Description) {
} else { sb.Description = newsb.Description
val _description = json.get("Description").asText("") varchanged = true
val _tag = json.get("TAG").asText("") }
val _category = json.get("Category").asText("") if (newsb.TAG != sb.TAG) {
val _language = json.get("Language").asText("") sb.TAG = newsb.TAG
val _path = json.get("Path").asText("") varchanged = true
var changed = false }
if (ValidString(_description) && _description != sb.Description) { if (newsb.ValidCategory()) {
sb.Description = _description if (newsb.Category != sb.Category){
changed = true sb.Category = newsb.Category
} varchanged = true
if (ValidString(_tag) && _tag != sb.TAG) { }
sb.TAG = _tag
changed = true
}
if (ValidString(_category) && _category != sb.Category) {
if (Category.entries.any { cat ->
cat.name == _category
}) {
sb.Category = _category
changed = true
} else { } else {
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Category"))) it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Category")))
return@patch return@patch
} }
} if (newsb.ValidLanguage()) {
if (ValidString(_language) && _language != sb.Language) { if (newsb.Language != sb.Language){
sb.Language = _language sb.Language = newsb.Language
changed = true varchanged = true
} }
if (ValidString(_path) && _path != sb.Path) {
if (ValidFile(_path)) {
sb.Path = _path
changed = true
} else { } else {
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Path, file does not exist"))) it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Language")))
return@patch return@patch
} }
} if (newsb.ValidVoiceType()) {
if (changed) { if (newsb.VoiceType != sb.VoiceType){
if (db.soundDB.UpdateByIndex(index.toInt(), sb)) { sb.VoiceType = newsb.VoiceType
db.soundDB.Resort() varchanged = true
it.result(objectmapper.writeValueAsString(resultMessage("OK"))) }
} else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to update soundbank with index $index"))) } else {
} else it.status(400) it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid VoiceType")))
.result(objectmapper.writeValueAsString(resultMessage("Nothing has changed for soundbank with index $index"))) return@patch
}
val newpath = Somecodes.SoundbankDirectory(newsb.Language, newsb.VoiceType, newsb.Category).resolve(newsb.Path)
if (Files.isRegularFile(newpath)){
if (newpath.toAbsolutePath().toString() != sb.Path) {
sb.Path = newpath.toAbsolutePath().toString()
varchanged = true
}
} else {
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid path")))
return@patch
}
if (varchanged) {
//println("Some fields changed for index=$index, updating...")
if (db.soundDB.UpdateByIndex(index.toInt(), sb)) {
db.soundDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to update soundbank with index $index")))
} else it.status(400)
.result(objectmapper.writeValueAsString(resultMessage("Nothing has changed for soundbank with index $index")))
} else Exception("Some fields are empty")
} catch(e: Exception){
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Unable to parse request body to Soundbank, Message: ${e.message}")))
} }
}
} } else it.status(404).result(objectmapper.writeValueAsString(resultMessage("Soundbank with index $index not found")))
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index")))
} }
get("ExportXLSX") { get("ExportXLSX") {
val xlsxdata = db.soundDB.Export_XLSX() val xlsxdata = db.soundDB.Export_XLSX()
@@ -639,7 +697,6 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
// get timer list // get timer list
it.result(MariaDB.ArrayListtoString(db.scheduleDB.List)) it.result(MariaDB.ArrayListtoString(db.scheduleDB.List))
} }
delete("List") { delete("List") {
// truncate timer table // truncate timer table
if (db.scheduleDB.Clear()) { if (db.scheduleDB.Clear()) {
@@ -670,8 +727,19 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
val zones = broadcast_zones.split(";") val zones = broadcast_zones.split(";")
if (zones.all { zz -> db.broadcastDB.List.any { xx -> xx.description.equals(zz,true) } }){ if (zones.all { zz -> db.broadcastDB.List.any { xx -> xx.description.equals(zz,true) } }){
if (ValidString(language) && Language.entries.any{ lang -> lang.name == language }){ if (ValidString(language) && Language.entries.any{ lang -> lang.name == language }){
val newvalue = ScheduleBank(0u, description, day, time, soundpath, repeat, enable, broadcast_zones, language) val newvalue = ScheduleBank(
0u,
description,
day,
time,
soundpath,
repeat,
enable,
broadcast_zones,
language
)
if (db.scheduleDB.Add(newvalue)){ if (db.scheduleDB.Add(newvalue)){
db.scheduleDB.Resort() db.scheduleDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK"))) it.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add schedule to database"))) } else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add schedule to database")))
@@ -814,16 +882,22 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
} }
} }
//TODO kirim list message dan broadcast zones untuk ADD/Edit schedule //Kirim list message dan broadcast zones untuk ADD/Edit schedule
//TODO add at js file for messagebank and broadcast zones values
get("GetMessageAndBroadcastZones") { get("GetMessageAndBroadcastZones") {
val result = object { val result = object {
//TODO filter message without input variable val messages = db.messageDB.List.filter { mb -> !mb.Message_Detail.contains("[") && !mb.Message_Detail.contains("]") }
val messages = db.messageDB.List
val broadcastzones = db.broadcastDB.List val broadcastzones = db.broadcastDB.List
} }
it.result(objectmapper.writeValueAsString(result)) it.result(objectmapper.writeValueAsString(result))
} }
// Kirim list language dari Messagebank berdasarkan ANN_ID
get("GetLanguageList/{ANN_ID}") { get1 ->
//kirim list language dari Messagebank
val langlist = db.messageDB.List.filter { it.ANN_ID == get1.pathParam("ANN_ID").toInt().toUInt() }.map { it.Language }.distinct()
get1.result(objectmapper.writeValueAsString(langlist))
}
} }
path("Log") { path("Log") {
get("List") { get1 -> get("List") { get1 ->
@@ -1126,9 +1200,11 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
// Steph : coba tambah untuk QueuePaging Table. Belum ada di JS file(?) // Steph : coba tambah untuk QueuePaging Table. Belum ada di JS file(?)
path("QueuePaging"){ path("QueuePaging"){
get("List"){ get("List"){
it.result(MariaDB.ArrayListtoString(db.queuepagingDB.List)) it.result(MariaDB.ArrayListtoString(db.queuepagingDB.List))
} }
delete("List"){ delete("List"){
// truncate queue paging table // truncate queue paging table
if (db.queuepagingDB.Clear()) { if (db.queuepagingDB.Clear()) {
@@ -1155,11 +1231,14 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
} }
// Steph : coba tambah untuk QueueTable Table. Belum ada di JS file(?) // Steph : coba tambah untuk QueueTable Table. Belum ada di JS file(?)
path("QueueTable"){ path("QueueTable"){
get("List"){ get("List"){
it.result(MariaDB.ArrayListtoString(db.queuetableDB.List)) it.result(MariaDB.ArrayListtoString(db.queuetableDB.List))
} }
delete("List"){ delete("List"){
// truncate queue table // truncate queue table
if (db.queuetableDB.Clear()) { if (db.queuetableDB.Clear()) {
@@ -1169,6 +1248,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate queue sound table"))) it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate queue sound table")))
} }
} }
delete("DeleteByIndex/{index}") { delete("DeleteByIndex/{index}") {
// delete by index // delete by index
val index = it.pathParam("index").toUIntOrNull() val index = it.pathParam("index").toUIntOrNull()