Feature/rework vue schema
This commit is contained in:
@@ -1,256 +0,0 @@
|
||||
<?php /** @noinspection PhpUndefinedClassInspection
|
||||
* @var string $mfLayoutPackage
|
||||
* @var TYPE_NAME $git_merge_ts
|
||||
*/
|
||||
|
||||
//additional css /css/views/RaspberryDisplay.css
|
||||
|
||||
$JSGlobals = ["BASE_URL" => self::getUrl("Domain"),
|
||||
"DASHBOARD_URL" => self::getUrl("Dashboard"),
|
||||
"MFAPPNAME" => MFAPPNAME_SLUG,
|
||||
"PAGE_TITLE" => "Domains",
|
||||
"PATH" => [
|
||||
["text" => MFAPPNAME_SLUG, "href" => self::getUrl("Dashboard")],
|
||||
["text" => "Domain Management", "href" => self::getUrl("Domain")],
|
||||
["text" => "Domains"]
|
||||
],
|
||||
"DOMAIN_API_URL" => self::getUrl("Domain/api"),
|
||||
];
|
||||
|
||||
$additionalJS = [
|
||||
"plugins/vue/vue.js",
|
||||
"plugins/axios/axios.min.js",
|
||||
"plugins/moment/moment.min.js",
|
||||
"plugins/daterangepicker/daterangepicker.js",
|
||||
"plugins/xlsx/xlsx.min.js",
|
||||
"plugins/vue/tt-components/tt-table.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/vue/tt-components/tt-select.js",
|
||||
"plugins/vue/tt-components/tt-datepicker.js",
|
||||
"plugins/vue/tt-components/tt-input.js",
|
||||
"plugins/vue/tt-components/tt-autocomplete.js",
|
||||
"plugins/vue/tt-components/tt-icon-select.js",
|
||||
"plugins/vue/tt-components/tt-number-range.js",
|
||||
];
|
||||
$additionalCSS = [
|
||||
"plugins/daterangepicker/daterangepicker.css",
|
||||
'plugins/vue/tt-components/css/tt-table.css',
|
||||
];
|
||||
|
||||
include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php"); ?>
|
||||
|
||||
<div id="app">
|
||||
<!-- start page title -->
|
||||
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
|
||||
|
||||
<tt-table :fetch-url="window['TT_CONFIG']['DOMAIN_API_URL'] + '?do=getDomains'" :config="domainsTableConfig"
|
||||
small ssr ref="table">
|
||||
<template v-slot:top-buttons>
|
||||
<button type="button" class="btn btn-primary" @click="reloadDomains">
|
||||
<template v-if="reloadDomainsLoading">
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
Reload Domains
|
||||
</template>
|
||||
</button>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" v-model="checkDomainInput" placeholder="Neue Domain überprüfen">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-primary" @click="checkDomainAvailability">
|
||||
<template v-if="checkDomainLoading">
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<i class="fas fa-search"></i>
|
||||
</template>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Slot to show DNS records button -->
|
||||
<template v-slot:inwxroid="{ row }">
|
||||
<button type="button" class="btn btn-primary" @click="showDnsRecordsModal(row.domain)"
|
||||
:class="dnsRecordsModalLoading === row.domain ? 'disabled' : ''">
|
||||
<template v-if="dnsRecordsModalLoading === row.domain">
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
</template>
|
||||
<span v-else>DNS</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- Registrant Admin Tech Billing from domainContacts -->
|
||||
<template v-slot:registrant="{ row }">
|
||||
{{ domainContacts[row.registrant] ? domainContacts[row.registrant]["name"] : '' }}
|
||||
</template>
|
||||
<template v-slot:admin="{ row }">{{ domainContacts[row.admin] ? domainContacts[row.admin]["name"] : ''}}
|
||||
</template>
|
||||
<template v-slot:tech="{ row }">{{ domainContacts[row.tech] ? domainContacts[row.tech]["name"] : ''}}
|
||||
</template>
|
||||
<template v-slot:billing="{ row }">{{ domainContacts[row.billing] ? domainContacts[row.billing]["name"] : ''}}
|
||||
</template>
|
||||
|
||||
</tt-table>
|
||||
|
||||
<!-- Bootstrap Modal to query and show all DNS records for a domain -->
|
||||
<div class="modal show d-block" tabindex="-1" role="dialog" style="background: rgba(0, 0, 0, 0.5);" ref="dnsRecordsModal" @click="dnsRecordsModal.domain = null" @keydown.esc="dnsRecordsModal.domain = null"
|
||||
v-if="dnsRecordsModal.domain">
|
||||
<div class="modal-dialog" role="document" style="max-width: fit-content" @click.stop>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">DNS Records for {{ dnsRecordsModal.domain ?? '' }}</h5>
|
||||
<button type="button" class="close" @click="dnsRecordsModal.domain = null">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table class="tt-table table-striped table-bordered table-hover table-sm table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Record Class</th>
|
||||
<th>Record Type</th>
|
||||
<th>Record Host</th>
|
||||
<th>Record Value</th>
|
||||
<th>Record TTL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="record in dnsRecordsModal.records">
|
||||
<td>{{ record.class }}</td>
|
||||
<td>{{ record.type }} {{ record.pri ? '(' + record.pri + ')' : '' }}</td>
|
||||
<td>{{ record.host }}</td>
|
||||
<td>{{ record.value }}</td>
|
||||
<td>{{ record.ttl }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @click="dnsRecordsModal.domain = null">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
window: window,
|
||||
domainContacts: {},
|
||||
reloadDomainsLoading: false,
|
||||
dnsRecordsModalLoading: null,
|
||||
dnsRecordsModal: {
|
||||
domain: null,
|
||||
records: []
|
||||
},
|
||||
checkDomainInput: '',
|
||||
checkDomainResult: null,
|
||||
checkDomainLoading: false
|
||||
},
|
||||
mounted() {
|
||||
this.fetchDomainContacts()
|
||||
},
|
||||
computed: {
|
||||
domainsTableConfig() {
|
||||
const base = {
|
||||
headers: [
|
||||
{text: "DNS", key: "inwxRoId", filter: false, sortable: false},
|
||||
{text: "Domain", key: "domain"},
|
||||
{text: "Plesk", key: "pleskId", filter: 'iconSelect', filterOptions: [
|
||||
{value: 1, text: 'Yes', icon: 'fas fa-check text-success'},
|
||||
{value: 0, text: 'No', icon: 'fas fa-times text-danger'}
|
||||
], sortable: false},
|
||||
{text: "Created Date", key: "crDate", filter: "date"},
|
||||
{text: "Expiration Date", key: "exDate", filter: "date"},
|
||||
{text: "Renewal Date", key: "reDate", filter: "date"},
|
||||
{text: "Updated Date", key: "upDate", filter: "date"},
|
||||
{text: "Transfer Lock", key: "transferLock", filter: 'iconSelect', filterOptions: [
|
||||
{value: 1, text: 'Locked', icon: 'fas fa-lock text-danger'},
|
||||
{value: 0, text: 'Unlocked', icon: 'fas fa-unlock text-success'}
|
||||
]},
|
||||
{text: "Authorization Code", key: "authCode", sortable: false},
|
||||
{text: "Registrant ID", key: "registrant", sortable: false},
|
||||
{text: "Admin ID", key: "admin", sortable: false},
|
||||
{text: "Tech ID", key: "tech", sortable: false},
|
||||
{text: "Billing ID", key: "billing", sortable: false},
|
||||
{text: "Name Servers", key: "ns"}
|
||||
],
|
||||
tableHeader: 'Bestellungen',
|
||||
}
|
||||
|
||||
const domainContactsSorted = Object.entries(this.domainContacts).sort(([, a], [, b]) => a.name.localeCompare(b.name))
|
||||
const domainContactsFilterOptions = domainContactsSorted.map(([, contact]) => {
|
||||
return {text: contact.name, value: contact.inwxRoId}
|
||||
})
|
||||
|
||||
// for registrant admin tech billing set filter to select with domainContacts if domainContacts is not empty
|
||||
if (Object.keys(this.domainContacts).length > 0) {
|
||||
base.headers = base.headers.map(header => {
|
||||
if (['registrant', 'admin', 'tech', 'billing'].includes(header.key)) {
|
||||
header.filter = 'select'
|
||||
header.filterOptions = domainContactsFilterOptions
|
||||
}
|
||||
return header
|
||||
})
|
||||
}
|
||||
return base
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async showDnsRecordsModal(domain) {
|
||||
this.dnsRecordsModalLoading = domain
|
||||
this.dnsRecordsModal = {
|
||||
domain: null,
|
||||
records: []
|
||||
}
|
||||
const response = await axios.get(window['TT_CONFIG']['DOMAIN_API_URL'] + '?do=getDnsRecords&domain=' + domain)
|
||||
this.dnsRecordsModal.domain = domain
|
||||
this.dnsRecordsModal.records = response.data.map(record => {
|
||||
if (typeof record.entries === 'object') {
|
||||
record.value = record.entries[0]
|
||||
} else {
|
||||
record.value = record.target || record.txt || record.ip
|
||||
}
|
||||
if (record.type === 'SOA') {
|
||||
record.value = record.mname + ' ' + record.rname + ' ' + record.serial + ' ' + record.refresh + ' ' + record.retry + ' ' + record.expire
|
||||
|
||||
}
|
||||
return record
|
||||
})
|
||||
this.dnsRecordsModalLoading = null
|
||||
this.$nextTick(() => {
|
||||
this.$refs.dnsRecordsModal.focus()
|
||||
})
|
||||
},
|
||||
async fetchDomainContacts() {
|
||||
const response = await axios.get(window['TT_CONFIG']['DOMAIN_API_URL'] + '?do=getDomainContacts')
|
||||
this.domainContacts = response.data
|
||||
},
|
||||
async reloadDomains() {
|
||||
this.reloadDomainsLoading = true
|
||||
const response = await axios.get(window['TT_CONFIG']['DOMAIN_API_URL'] + '?do=importAllDomains')
|
||||
window.notify('success', response.data["importMessages"].join('<br>'))
|
||||
await Promise.all([
|
||||
this.fetchDomainContacts(),
|
||||
this.$refs.table.fetchData(this.$refs.table.pagination.page)
|
||||
])
|
||||
this.reloadDomainsLoading = false
|
||||
},
|
||||
//TODO: make this cleaner
|
||||
async checkDomainAvailability() {
|
||||
this.checkDomainLoading = true
|
||||
const response = await axios.get(window['TT_CONFIG']['DOMAIN_API_URL'] + '?do=checkDomain&domain=' + this.checkDomainInput)
|
||||
const priceInformation = response.data.price.domain[this.checkDomainInput]
|
||||
window.notify(response.data.status === 'free' ? 'success' : 'error',
|
||||
`Domain ist ${response.data.status === 'free' ? 'verfügbar. Registrieren um' : 'nicht frei. Transfer um'} ${priceInformation.price}${priceInformation.currency}/${priceInformation.period === '1Y' ? 'Jahr' : priceInformation.period}`)
|
||||
this.checkDomainLoading = false
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
<?php /** @noinspection PhpUndefinedClassInspection
|
||||
* @var string $mfLayoutPackage
|
||||
* @var TYPE_NAME $git_merge_ts
|
||||
*/
|
||||
|
||||
//additional css /css/views/RaspberryDisplay.css
|
||||
|
||||
$JSGlobals = ["BASE_URL" => self::getUrl("Domain"),
|
||||
"DASHBOARD_URL" => self::getUrl("Dashboard"),
|
||||
"MFAPPNAME" => MFAPPNAME_SLUG,
|
||||
"PAGE_TITLE" => "Historische Tickets",
|
||||
"PATH" => [
|
||||
["text" => MFAPPNAME_SLUG, "href" => self::getUrl("Dashboard")],
|
||||
["text" => "Historische Tickets", "href" => self::getUrl("HistoricTicket")]
|
||||
],
|
||||
"HISTORIC_TICKET_API_URL" => self::getUrl("HistoricTicket/api"),
|
||||
];
|
||||
|
||||
$additionalJS = [
|
||||
"plugins/vue/vue.js",
|
||||
"plugins/axios/axios.min.js",
|
||||
"plugins/moment/moment.min.js",
|
||||
"plugins/daterangepicker/daterangepicker.js",
|
||||
"plugins/xlsx/xlsx.min.js",
|
||||
"plugins/vue/tt-components/tt-table.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/vue/tt-components/tt-select.js",
|
||||
"plugins/vue/tt-components/tt-datepicker.js",
|
||||
"plugins/vue/tt-components/tt-input.js",
|
||||
"plugins/vue/tt-components/tt-autocomplete.js",
|
||||
"plugins/vue/tt-components/tt-icon-select.js",
|
||||
"plugins/vue/tt-components/tt-number-range.js",
|
||||
];
|
||||
$additionalCSS = [
|
||||
"plugins/daterangepicker/daterangepicker.css",
|
||||
'plugins/vue/tt-components/css/tt-table.css',
|
||||
];
|
||||
|
||||
include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php"); ?>
|
||||
|
||||
<div id="app">
|
||||
<!-- start page title -->
|
||||
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
|
||||
|
||||
<tt-table :fetch-url="window['TT_CONFIG']['HISTORIC_TICKET_API_URL'] + '?do=getHistoricTickets'"
|
||||
:config="historicTicketTableConfig"
|
||||
small ssr ref="table">
|
||||
<template v-slot:top-buttons>
|
||||
<!-- add input for global search with label and bootstrap class-->
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" class="form-control" placeholder="Globale Suche" v-model="globalSearch" @keydown.enter="doGlobalSearch">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-primary" type="button" @click="doGlobalSearch">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:first_name="{ row }">
|
||||
{{ row.first_name }} {{ row.last_name }}
|
||||
</template>
|
||||
|
||||
<template v-slot:ctime="{ row }">
|
||||
{{ new Date(row.ctime * 1000).toLocaleString() }}
|
||||
</template>
|
||||
|
||||
<template v-slot:ticket_number="{ row }">
|
||||
<a href="#" @click="clickTicketNumber(row.ticket_number)">{{ row.ticket_number }}</a>
|
||||
</template>
|
||||
|
||||
</tt-table>
|
||||
|
||||
|
||||
<!-- Bootstrap Modal to show global search results -->
|
||||
<div class="modal show d-block" tabindex="0" role="dialog" style="background: rgba(0, 0, 0, 0.5);" @click="globalSearchModal = false" @keydown.esc="globalSearchModal = false" ref="globalSearchModal"
|
||||
v-if="globalSearchModal">
|
||||
<div class="modal-dialog" role="document" @click.stop
|
||||
style="width:fit-content;max-width: 80vw ; max-height: 80vh; overflow-y: auto;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Suchergebnisse</h5>
|
||||
<button type="button" class="close" @click="globalSearchModal = false">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<tt-table
|
||||
:fetch-url="`${window['TT_CONFIG']['HISTORIC_TICKET_API_URL']}?do=findHistoricTicket&query=${globalSearch}`"
|
||||
:config="globalSearchModalTableConfig"
|
||||
small ref="table">
|
||||
|
||||
<template v-slot:ctime="{ row }">
|
||||
{{ window.moment(row.ctime * 1000).format('DD.MM.YYYY HH:mm') }}
|
||||
</template>
|
||||
|
||||
<template v-slot:ticket_number="{ row }">
|
||||
<a href="#" @click="clickTicketNumber(row.ticket_number)">{{ row.ticket_number }}</a>
|
||||
</template>
|
||||
</tt-table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @click="globalSearchModal = false">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap Modal to show ticket messages -->
|
||||
<div class="modal show d-block" tabindex="0" role="dialog" style="background: rgba(0, 0, 0, 0.5);" @click="selectedTicketNumber = null" @keydown.esc="selectedTicketNumber = null" ref="selectedTicketModal"
|
||||
v-if="selectedTicketNumber">
|
||||
<div class="modal-dialog" role="document" @click.stop
|
||||
style="width:fit-content;max-width: 80vw ; max-height: 80vh; overflow-y: auto;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Ticket {{ selectedTicketNumber }}</h5>
|
||||
<button type="button" class="close" @click="selectedTicketNumber = null">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div v-if="selectedTicketData">
|
||||
<h5>{{ selectedTicketData.ticket.subject }}</h5>
|
||||
<p>{{ selectedTicketData.ticket.message }}</p>
|
||||
<div v-for="message in selectedTicketData.messages">
|
||||
<hr>
|
||||
<h6>{{ new Date(message.ctime * 1000).toLocaleString()}}</h6>
|
||||
<p style="word-break: break-all;" v-html="message.content?.replaceAll('\n', '<br>')"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="spinner-border text-primary" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
window: window,
|
||||
selectedTicketNumber: null,
|
||||
selectedTicketData: null,
|
||||
globalSearch: '',
|
||||
globalSearchModal: false,
|
||||
globalSearchModalTableConfig: {
|
||||
headers: [
|
||||
{text: 'Ticket Number', key: 'ticket_number', filter: false, sortable: false},
|
||||
{text: 'Erstellt', key: 'ctime', filter: false, sortable: false},
|
||||
{text: 'Subject', key: 'ticket_subject', filter: false, sortable: false},
|
||||
{text: 'Message', key: 'ticket_message', filter: false, sortable: false},
|
||||
],
|
||||
tableHeader: 'Suchergebnisse',
|
||||
key: 'HistoricTicketGlobalSearch',
|
||||
},
|
||||
historicTicketTableConfig: {
|
||||
headers: [
|
||||
{text: 'Ticket Number', key: 'ticket_number', filter: 'search'},
|
||||
{text: 'Erstellt', key: 'ctime', filter: false},
|
||||
{text: 'Subject', key: 'subject', filter: 'search', sortable: false},
|
||||
{text: 'Type', key: 'type', filter: 'select', filterOptions: [
|
||||
{value: 'BACKOFFICE', text: 'BACKOFFICE'},
|
||||
{value: 'KUNDENANFRAGEN', text: 'KUNDENANFRAGEN'},
|
||||
{value: 'STÖRUNGEN', text: 'STÖRUNGEN'},
|
||||
{value: 'ALLGEMEINES', text: 'ALLGEMEINES'},
|
||||
{value: 'TERMIN VEREINBART', text: 'TERMIN VEREINBART'},
|
||||
{value: 'VERRECHNEN AB DATUM', text: 'VERRECHNEN AB DATUM'},
|
||||
{value: 'ONLINE-TICKETS', text: 'ONLINE-TICKETS'},
|
||||
{value: 'KÜNDIGUNG', text: 'KÜNDIGUNG'},
|
||||
{value: 'BESTELLUNGEN', text: 'BESTELLUNGEN'},
|
||||
{value: 'PORTIERUNG', text: 'PORTIERUNG'},
|
||||
{value: 'KABEL-TV', text: 'KABEL-TV'},
|
||||
{value: 'TIEFBAU', text: 'TIEFBAU'},
|
||||
{value: 'ENERGIE STEIERMARK', text: 'ENERGIE STEIERMARK'},
|
||||
{value: 'INTERN', text: 'INTERN'},
|
||||
{value: 'FELIX', text: 'FELIX'},
|
||||
{value: '0', text: '0'},
|
||||
{value: 'JETTEN', text: 'JETTEN'},
|
||||
]},
|
||||
{text: 'Status', key: 'status', filter: 'select', filterOptions: [
|
||||
{value: 'Geschlossen', text: 'Geschlossen'},
|
||||
{value: 'In Evidenz', text: 'In Evidenz'},
|
||||
{value: 'In Bearbeitung', text: 'In Bearbeitung'},
|
||||
{value: 'Business In Bearbeitung', text: 'Business In Bearbeitung'},
|
||||
{value: 'Business Angebot gelegt', text: 'Business Angebot gelegt'},
|
||||
]},
|
||||
{text: 'Name', key: 'first_name', filter: 'search', sortable: false},
|
||||
{text: 'Email', key: 'email', filter: 'search', sortable: false},
|
||||
{text: 'Phone', key: 'phone', filter: 'search', sortable: false},
|
||||
],
|
||||
defaultPageSize: 25,
|
||||
tableHeader: 'Historische Tickets',
|
||||
key: 'HistoricTicket',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async doGlobalSearch() {
|
||||
if (this.globalSearch.length > 0) {
|
||||
this.globalSearchModal = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.globalSearchModal.focus();
|
||||
});
|
||||
}
|
||||
},
|
||||
async clickTicketNumber(ticketNumber) {
|
||||
this.globalSearchModal = false;
|
||||
this.selectedTicketData = null;
|
||||
|
||||
this.selectedTicketNumber = ticketNumber;
|
||||
const response = await axios.post(`${window['TT_CONFIG']['HISTORIC_TICKET_API_URL']}?do=getHistoricTicketMessages`, {ticketNumber});
|
||||
this.selectedTicketData = response.data;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.selectedTicketModal.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
<?php /** @noinspection PhpUndefinedClassInspection
|
||||
* @var string $mfLayoutPackage
|
||||
* @var TYPE_NAME $git_merge_ts
|
||||
*/
|
||||
|
||||
//additional css /css/views/RaspberryDisplay.css
|
||||
|
||||
$JSGlobals = ["BASE_URL" => self::getUrl("RaspberryDisplay"),
|
||||
"DASHBOARD_URL" => self::getUrl("Dashboard"),
|
||||
"MFAPPNAME" => MFAPPNAME_SLUG,
|
||||
"PAGE_TITLE" => "Raspberry Displays",
|
||||
"PATH" => [
|
||||
["text" => MFAPPNAME_SLUG, "href" => self::getUrl("Dashboard")],
|
||||
["text" => "Raspberry Displays", "href" => self::getUrl("RaspberryDisplay")]
|
||||
]
|
||||
];
|
||||
|
||||
$additionalJS = ["plugins/vue/vue.min.js",
|
||||
"plugins/axios/axios.min.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/vue/tt-components/tt-loader.js"];
|
||||
$additionalCSS = ["css/views/RaspberryDisplay.css", "plugins/vue/tt-components/css/tt-loader.css"];
|
||||
|
||||
include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php"); ?>
|
||||
|
||||
<div id="app">
|
||||
<!-- start page title -->
|
||||
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
|
||||
|
||||
<div class="card">
|
||||
<tt-loader v-if="loading"></tt-loader>
|
||||
|
||||
<div class="p-2">
|
||||
<h3>8322 Studenzen NOC Displays</h3>
|
||||
|
||||
<div class="display-grid">
|
||||
<div v-for="display in displays" :key="display.id"
|
||||
:class="['display', display['display_label'].includes('-B-') ? 'big-42-inch' : 'small-27-inch']"
|
||||
:style="display['custom_style']" style="">
|
||||
<div style="display: grid; grid-template-columns: max-content auto max-content; justify-items: center;width:100%; padding: 0 2px">
|
||||
<div>
|
||||
<!-- FONT AWESOME ONLINE GREEN CIRCLE -->
|
||||
<i class="fas fa-circle" data-toggle="tooltip" title="ONLINE" style="color: green"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div @click.prevent="enableDisplayURLEditMode(display.id)" style="cursor: pointer">
|
||||
<span v-if="displaysURLEditMode !== display.id">{{ display['display_url'] | cleanupURL }}</span>
|
||||
<input v-else-if="displaysURLEditMode === display.id"
|
||||
v-model="display['display_url']"
|
||||
@keyup.enter="disableDisplayURLEditMode(display.id, display['display_url'])"
|
||||
@blur="disableDisplayURLEditMode(display.id, display['display_url'])"
|
||||
ref="displayURLEditInput"
|
||||
class="form-control"
|
||||
type="text">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div style="cursor: pointer">
|
||||
<!-- FONT AWESOME REBOOT ICON -->
|
||||
<i class="fas fa-red fa-sync-alt" data-toggle="tooltip" title="Reboot this Raspberry"
|
||||
@click="rebootRaspberry(display.id)"
|
||||
style="color: green"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- Checkbox for Auto Refresh Enabled -->
|
||||
<div style="display: inline-block" data-toggle="tooltip"
|
||||
:title="`Auto refresh is ${display['auto_refresh_enabled'] ? 'enabled' : 'disabled'}.`">
|
||||
<input type="checkbox" :id="'auto_refresh_enabled_checkbox_' + display.id"
|
||||
v-model="display['auto_refresh_enabled']"
|
||||
@change="submitChanges(display.id, 'auto_refresh_enabled', display['auto_refresh_enabled'])">
|
||||
<label :for="'auto_refresh_enabled_checkbox_' + display.id">ARF</label>
|
||||
</div>
|
||||
|
||||
<!-- This will only display if both are true, consider adjusting logic as needed -->
|
||||
<span style="margin: 0 4px"> | </span>
|
||||
|
||||
<!-- Checkbox for Margin Hotfix Enabled -->
|
||||
<div style="display: inline-block" data-toggle="tooltip"
|
||||
:title="`Margin Hotfix is ${display['margin_hot_fix_enabled'] ? 'enabled' : 'disabled'}.`">
|
||||
|
||||
<input type="checkbox" :id="'margin_hot_fix_enabled_checkbox_' + display.id"
|
||||
v-model="display['margin_hot_fix_enabled']"
|
||||
@change="submitChanges(display.id, 'margin_hot_fix_enabled', display['margin_hot_fix_enabled'])">
|
||||
<label :for="'margin_hot_fix_enabled_checkbox_' + display.id">MHF</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-text="display['display_label']"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="<?=self::getResourcePath()?>js/pages/raspberryDisplay.js?<?=$git_merge_ts?>"></script>
|
||||
@@ -1,163 +0,0 @@
|
||||
<?php /** @noinspection PhpUndefinedClassInspection
|
||||
* @var string $mfLayoutPackage
|
||||
* @var TYPE_NAME $git_merge_ts
|
||||
*/
|
||||
|
||||
//additional css /css/views/RaspberryDisplay.css
|
||||
|
||||
$JSGlobals = ["BASE_URL" => self::getUrl("VoiceCallActive"),
|
||||
"DASHBOARD_URL" => self::getUrl("Dashboard"),
|
||||
"MFAPPNAME" => MFAPPNAME_SLUG,
|
||||
"PAGE_TITLE" => "Active Voice Calls",
|
||||
"PATH" => [
|
||||
["text" => MFAPPNAME_SLUG, "href" => self::getUrl("Dashboard")],
|
||||
["text" => "Active Voice Calls", "href" => self::getUrl("VoiceCallActive")]
|
||||
],
|
||||
"VOICE_CALL_ACTIVE_API_URL" => self::getUrl("VoiceCallActive/api"),
|
||||
];
|
||||
|
||||
$additionalJS = [
|
||||
"plugins/vue/vue.js",
|
||||
"plugins/axios/axios.min.js",
|
||||
"plugins/moment/moment.min.js",
|
||||
"plugins/daterangepicker/daterangepicker.js",
|
||||
"plugins/xlsx/xlsx.min.js",
|
||||
"plugins/vue/tt-components/tt-table.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/vue/tt-components/tt-select.js",
|
||||
"plugins/vue/tt-components/tt-datepicker.js",
|
||||
"plugins/vue/tt-components/tt-input.js",
|
||||
"plugins/vue/tt-components/tt-autocomplete.js",
|
||||
"plugins/vue/tt-components/tt-icon-select.js",
|
||||
"plugins/vue/tt-components/tt-number-range.js",
|
||||
];
|
||||
$additionalCSS = [
|
||||
"plugins/daterangepicker/daterangepicker.css",
|
||||
'plugins/vue/tt-components/css/tt-table.css',
|
||||
];
|
||||
|
||||
include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php"); ?>
|
||||
|
||||
<div id="app">
|
||||
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
|
||||
|
||||
<tt-table :fetch-url="window['TT_CONFIG']['VOICE_CALL_ACTIVE_API_URL'] + '?do=getActiveCalls'" :config="VoiceCallActiveTableConfig"
|
||||
small ssr ref="table">
|
||||
|
||||
<template v-slot:top-buttons>
|
||||
<button type="button" class="btn btn-primary" @click="refresh" data-toggle="tooltip" data-placement="bottom" title="Refreshing too often will run into API-Rate limits and will cause errors.">
|
||||
<template v-if="refreshLoading">
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
Refresh
|
||||
</template>
|
||||
</button>
|
||||
|
||||
<div class="d-flex">
|
||||
<label style="margin-bottom: 0 !important;">
|
||||
<input type="checkbox" id="autoRefresh" data-toggle="toggle" data-size="lg" @change="clickedCheckBox(this.checked)">
|
||||
<span class="ml-2">Auto Refresh (5sec)</span>
|
||||
</label>
|
||||
<span style="width: 50px"></span>
|
||||
<div class="voice-yellow p-2">Ringing</div>
|
||||
<div class="voice-red p-2">Outgoing</div>
|
||||
<div class="voice-green p-2">Ingoing</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:answer_time="{ row }">
|
||||
{{ !isNaN(new Date(row.answer_time)) ? window.moment(row.answer_time, 'YYYY-MM-DD HH:mm:ss Z').format('DD.MM.YYYY HH:mm:ss') : 'Call is not running' }}
|
||||
</template>
|
||||
|
||||
<template v-slot:status="{ row }">
|
||||
<i v-if="!row.dst_device_extension && row.status !== 'Ringing'" class="fas fa-phone-arrow-up-right"></i>
|
||||
<i v-else-if="!row.dst_device_extension && row.status === 'Ringing'" class="fas fa-phone-arrow-up-right fa-shake"></i>
|
||||
<i v-else-if="row.status === 'Ringing'" class="fas fa-phone-arrow-down-left fa-shake"></i>
|
||||
<i v-else class="fas fa-phone-arrow-down-left"></i>
|
||||
{{ row.status }}
|
||||
</template>
|
||||
|
||||
</tt-table>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.voice-green {
|
||||
background-color: #6CAE75 !important
|
||||
}
|
||||
.voice-yellow {
|
||||
background-color: #E8E288 !important
|
||||
}
|
||||
.voice-red {
|
||||
background-color: #FF8360 !important
|
||||
}
|
||||
|
||||
.table-sm td {
|
||||
padding: 4px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
window: window,
|
||||
VoiceCallActiveTableConfig: {
|
||||
customRowClass: function (row) {
|
||||
if (row.status.toLowerCase() === 'ringing') {
|
||||
return 'voice-yellow';
|
||||
}
|
||||
if (!row.dst_device_extension) {
|
||||
return 'voice-red';
|
||||
}
|
||||
if (row.status.toLowerCase() === 'answered') {
|
||||
return 'voice-green';
|
||||
}
|
||||
},
|
||||
headers: [
|
||||
{text: 'Call ID', key: 'id', filter: false, sortable: false},
|
||||
{text: 'Status', key: 'status', filter: false, sortable: false},
|
||||
{text: 'Answer Time', key: 'answer_time', filter: false, sortable: false},
|
||||
{text: 'Duration', key: 'duration', filter: false, sortable: false},
|
||||
{text: 'Source', key: 'src', filter: false, sortable: false},
|
||||
{text: 'Device Type', key: 'device_type', filter: false, sortable: false},
|
||||
{text: 'Destination', key: 'localized_dst', filter: false, sortable: false},
|
||||
{text: 'Destination User', key: 'dst_user', filter: false, sortable: false},
|
||||
{text: 'Destination Device Extension', key: 'dst_device_extension', filter: false, sortable: false},
|
||||
],
|
||||
tableHeader: 'Active Voice Calls',
|
||||
},
|
||||
refreshLoading: false,
|
||||
autoRefresh: null,
|
||||
},
|
||||
mounted() {
|
||||
//TODO: create vue tooltip component
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
const _this = this;
|
||||
$('#autoRefresh').change(function () {
|
||||
console.log(this.checked);
|
||||
if (this.checked) {
|
||||
_this.autoRefresh = setInterval(function () {
|
||||
_this.refresh();
|
||||
}, 5000);
|
||||
} else {
|
||||
clearInterval(_this.autoRefresh);
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
async refresh() {
|
||||
this.refreshLoading = true;
|
||||
this.$refs.table.loading = true;
|
||||
await this.$refs.table.fetchData();
|
||||
$('.tooltip').tooltip('hide');
|
||||
this.$refs.table.loading = false;
|
||||
this.refreshLoading = false;
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
<?php /** @noinspection PhpUndefinedClassInspection
|
||||
* @var string $mfLayoutPackage
|
||||
* @var TYPE_NAME $git_merge_ts
|
||||
*/
|
||||
|
||||
//additional css /css/views/RaspberryDisplay.css
|
||||
|
||||
$JSGlobals = ["BASE_URL" => self::getUrl("Domain"),
|
||||
"DASHBOARD_URL" => self::getUrl("Dashboard"),
|
||||
"MFAPPNAME" => MFAPPNAME_SLUG,
|
||||
"PAGE_TITLE" => "Domains",
|
||||
"PATH" => [
|
||||
["text" => MFAPPNAME_SLUG, "href" => self::getUrl("Dashboard")],
|
||||
["text" => "Voice Calls History", "href" => self::getUrl("VoiceCallHistory")]
|
||||
],
|
||||
"VOICE_CALL_HISTORY_API_URL" => self::getUrl("VoiceCallHistory/api"),
|
||||
];
|
||||
|
||||
$additionalJS = [
|
||||
"plugins/vue/vue.js",
|
||||
"plugins/axios/axios.min.js",
|
||||
"plugins/moment/moment.min.js",
|
||||
"plugins/daterangepicker/daterangepicker.js",
|
||||
"plugins/xlsx/xlsx.min.js",
|
||||
"plugins/vue/tt-components/tt-table.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/vue/tt-components/tt-select.js",
|
||||
"plugins/vue/tt-components/tt-datepicker.js",
|
||||
"plugins/vue/tt-components/tt-input.js",
|
||||
"plugins/vue/tt-components/tt-autocomplete.js",
|
||||
"plugins/vue/tt-components/tt-icon-select.js",
|
||||
"plugins/vue/tt-components/tt-number-range.js",
|
||||
];
|
||||
$additionalCSS = [
|
||||
"plugins/daterangepicker/daterangepicker.css",
|
||||
'plugins/vue/tt-components/css/tt-table.css',
|
||||
];
|
||||
|
||||
include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php"); ?>
|
||||
|
||||
<div id="app">
|
||||
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
|
||||
|
||||
<tt-table :fetch-url="window['TT_CONFIG']['VOICE_CALL_HISTORY_API_URL'] + '?do=getCalls'" :config="VoiceCallHistoryTableConfig"
|
||||
small ssr ref="table">
|
||||
<template v-slot:top-buttons>
|
||||
<button type="button" class="btn btn-primary" @click="importCallsFromToday">
|
||||
<template v-if="importCallsFromTodayLoading">
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
Re-Import Calls from Today
|
||||
</template>
|
||||
</button>
|
||||
|
||||
</template>
|
||||
</tt-table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
window: window,
|
||||
VoiceCallHistoryTableConfig: {
|
||||
headers: [
|
||||
{text: "Call-ID", key: "uid"},
|
||||
{text: "Voice Account", key: "voice_account"},
|
||||
{text: "Time Range", key: "start", filter: "date"},
|
||||
{text: "Source", key: "source"},
|
||||
{text: "Destination", key: "destination"},
|
||||
{text: "Billable", key: "billable", filter: "iconSelect", filterOptions: [
|
||||
{value: 1, text: 'Yes', icon: 'fas fa-check text-success'},
|
||||
{value: 0, text: 'No', icon: 'fas fa-times text-danger'}
|
||||
]},
|
||||
{text: "Duration", key: "duration"},
|
||||
],
|
||||
tableHeader: 'Voice Call History',
|
||||
key: 'VoiceCallHistory',
|
||||
},
|
||||
importCallsFromTodayLoading: false,
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
async importCallsFromToday() {
|
||||
this.importCallsFromTodayLoading = true;
|
||||
const response = await axios.get(window['TT_CONFIG']['VOICE_CALL_HISTORY_API_URL'] + '?do=importCallsFromToday');
|
||||
window.notify(response.data.status === 'success' ? 'success' : 'error', response.data.message);
|
||||
await this.$refs.table.fetchData();
|
||||
this.importCallsFromTodayLoading = false;
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>
|
||||
|
||||
49
Layout/default/VueViews/Vue.php
Normal file
49
Layout/default/VueViews/Vue.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
if (!isset($vueViewName)) {
|
||||
die("vueViewName is not set");
|
||||
}
|
||||
|
||||
if (!isset($mfLayoutPackage)) {
|
||||
die("mfLayoutPackage is not set");
|
||||
}
|
||||
|
||||
$additionalCSS = $additionalCSS ?? [];
|
||||
$additionalJS = $additionalJS ?? [];
|
||||
|
||||
$additionalJS = [
|
||||
...$additionalJS,
|
||||
"bundler.php",
|
||||
"js/pages/" . $vueViewName . "/" . $vueViewName . ".js",
|
||||
];
|
||||
|
||||
$additionalCSS = [
|
||||
...$additionalCSS,
|
||||
'plugins/daterangepicker/daterangepicker.css',
|
||||
'plugins/vue/tt-components/css/tt-table.css',
|
||||
'plugins/vue/tt-components/css/tt-loader.css',
|
||||
];
|
||||
|
||||
/**
|
||||
* Convert PascalCase to snake_case (e.g. PascalCase to pascal-case)
|
||||
* @param $str string PascalCase string
|
||||
* @return string snake-case string
|
||||
*/
|
||||
function pascalToSnakeCase(string $str): string {
|
||||
$snakeCase = preg_replace('/(?<!^)([A-Z])/', '-$1', $str);
|
||||
return strtolower($snakeCase);
|
||||
}
|
||||
|
||||
$vueTagName = pascalToSnakeCase($vueViewName);
|
||||
|
||||
include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/vueHeader.php"); ?>
|
||||
|
||||
|
||||
<div id="app">
|
||||
<<?php echo $vueTagName; ?>></<?php echo $vueTagName; ?>>
|
||||
</div>
|
||||
<script>
|
||||
const view = new Vue({el: '#app'});
|
||||
</script>
|
||||
|
||||
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>
|
||||
@@ -16,14 +16,14 @@
|
||||
<?php else: ?>
|
||||
<?php if(!$me->is("Admin")): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="<?=self::getUrl("Dashboard")?>"><i class="fe-airplay"></i> Dashboard</a>
|
||||
<a href="<?=self::getUrl("Dashboard")?>"><i class="fa-solid fa-airplay"></i> Dashboard</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<?php if($me->is("Admin")): ?>
|
||||
<li class="has-submenu">
|
||||
<a href="<?=self::getUrl("Dashboard")?>"><i class="fe-airplay"></i> Dashboard <div class="arrow-down"></div></a>
|
||||
<a href="<?=self::getUrl("Dashboard")?>"><i class="fa-solid fa-airplay"></i> Dashboard <div class="arrow-down"></div></a>
|
||||
<ul class="submenu">
|
||||
<li><a href="<?=self::getUrl("News")?>"><i class="far fa-fw fa-th-list text-info"></i> News</a></li>
|
||||
<?php if($me->is("employee")): ?>
|
||||
@@ -114,6 +114,7 @@
|
||||
<?php if($me->isAdmin() || $me->can("Cpeprovisioning")): ?><li><a href="<?=self::getUrl("Cpeprovisioning")?>"><i class="fad fa-fw fa-hdd text-info"></i> CPE Provisioning</a></li><?php endif; ?>
|
||||
<?php if($me->isAdmin() || $me->can("Cpeshipping")): ?><li><a href="<?=self::getUrl("Cpeshipping")?>"><i class="fad fa-fw fa-shipping-fast text-info"></i> CPE Versand</a></li><?php endif; ?>
|
||||
<?php if($me->isAdmin()) : ?><li><a href="<?=self::getUrl("Domain")?>"><i class="fad fa-fw fa-globe text-info"></i> Domains</a></li><?php endif; ?>
|
||||
<?php if($me->isAdmin()) : ?><li><a href="<?=self::getUrl("IpNetwork")?>"><i class="fa-solid fa-network-wired text-info"></i> IPAM</a></li><?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
77
Layout/default/vueHeader.php
Normal file
77
Layout/default/vueHeader.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php /** @var TYPE_NAME $git_merge_ts */ ?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title><?=MFAPPNAME_FULL?></title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<!-- App favicon -->
|
||||
<link rel="shortcut icon" href="<?=self::getResourcePath()?>assets/images/favicon.ico">
|
||||
|
||||
<link href="<?=self::getResourcePath()?>fontawesome/css/all.min.css?<?=$git_merge_ts?>" rel="stylesheet" type="text/css" />
|
||||
<link href="<?=self::getResourcePath()?>fontawesome/css/sharp-solid.css?<?=$git_merge_ts?>" rel="stylesheet" type="text/css" />
|
||||
<link href="<?=self::getResourcePath()?>fontawesome/css/sharp-regular.css?<?=$git_merge_ts?>" rel="stylesheet" type="text/css" />
|
||||
<link href="<?=self::getResourcePath()?>fontawesome/css/sharp-light.css?<?=$git_merge_ts?>" rel="stylesheet" type="text/css" />
|
||||
<link href="<?=self::getResourcePath()?>fontawesome/css/sharp-thin.css?<?=$git_merge_ts?>" rel="stylesheet" type="text/css" />
|
||||
<link href="<?=self::getResourcePath()?>assets/css/bootstrap.min.css?<?=$git_merge_ts?>" rel="stylesheet" type="text/css" />
|
||||
<link href="<?=self::getResourcePath()?>assets/css/app.min.css?<?=$git_merge_ts?>" rel="stylesheet" type="text/css" />
|
||||
<link href="<?=self::getResourcePath()?>plugins/notification/notify.min.css" rel="stylesheet" type="text/css" />
|
||||
<link href="<?=self::getResourcePath()?>assets/css/thetool.css?<?=$git_merge_ts?>" rel="stylesheet" type="text/css" />
|
||||
|
||||
|
||||
|
||||
<?php if(isset($additionalCSS) && is_array($additionalCSS) && count($additionalCSS)): ?>
|
||||
<?php foreach($additionalCSS as $css): ?>
|
||||
<link rel="stylesheet" href="<?=self::getResourcePath()?><?=$css?>?<?=$git_merge_ts?>" />
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.mfNotify = <?=isset($mfNotify) ? json_encode($mfNotify) : "null"; ?>;
|
||||
window.TT_CONFIG = {};
|
||||
|
||||
<?php
|
||||
if(isset($JSGlobals) && is_array($JSGlobals) && count($JSGlobals)):
|
||||
foreach($JSGlobals as $key => $value): ?>
|
||||
window.TT_CONFIG.<?=$key?> = <?=is_array($value) ? json_encode($value) : "'$value'"; ?>;
|
||||
<?php endforeach; endif;?>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="<?=self::getResourcePath()?>js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="<?=self::getResourcePath()?>js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="<?=self::getResourcePath()?>js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="<?=self::getResourcePath()?>assets/js/bootstrap-select.min.js"></script>
|
||||
<script type="text/javascript" src="<?=self::getResourcePath()?>plugins/notification/notify.js"></script>
|
||||
|
||||
<?php if(isset($additionalJS) && is_array($additionalJS) && count($additionalJS)): ?>
|
||||
<?php foreach($additionalJS as $js): ?>
|
||||
<script src="<?=self::getResourcePath()?><?=$js?>?<?=$git_merge_ts?>"></script>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if(MFAPPNAME == "devthetool"): ?>
|
||||
<style type="text/css">
|
||||
body {
|
||||
border-left: 8px dashed #f672a7;
|
||||
}
|
||||
</style>
|
||||
<?php endif; ?>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
baseurl = '<?=self::getResourcePath()?>';
|
||||
</script>
|
||||
|
||||
<!-- Navigation Bar-->
|
||||
<header id="topnav">
|
||||
<?php include(realpath(dirname(__FILE__)."/")."/topbar.php"); ?>
|
||||
<?php include(realpath(dirname(__FILE__)."/")."/menu.php"); ?>
|
||||
</header>
|
||||
<!-- End Navigation Bar-->
|
||||
|
||||
|
||||
<div class="wrapper pl-0 pl-lg-1 pr-0 pr-lg-1 ">
|
||||
<div class="container-fluid">
|
||||
Reference in New Issue
Block a user