Reworked Device Module and Vue Components

This commit is contained in:
2024-05-28 09:12:13 +02:00
parent fa3c1b8766
commit e1cf5d5d9e
21 changed files with 321 additions and 439 deletions

View File

@@ -1,94 +0,0 @@
<?php
$pagination_baseurl = $this->getUrl($Mod, "Index");
$pagination_baseurl_params = ["filter" => $filter];
$pagination_entity_name = "Devicemanufactor";
?>
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php"); ?>
<link href="<?= self::getResourcePath() ?>assets/css/datatables-std.css?<?=date('U')?>" rel="stylesheet" type="text/css"/>
<!-- start page title -->
<div class="row">
<div class="col-12">
<div class="page-title-box">
<div class="page-title-right">
<ol class="breadcrumb m-0">
<li class="breadcrumb-item"><a href="<?= self::getUrl("Dashboard") ?>"><?= MFAPPNAME_SLUG ?></a>
</li>
<li class="breadcrumb-item active">Gerätehersteller</li>
</ol>
</div>
<h4 class="page-title">Gerätehersteller</h4>
</div>
</div>
</div>
<!-- end page title -->
<div class="card">
<div class="card-body mb-3">
<div class="row">
<div class="col-12">
<div class="float-left">
<h4 class="header-title">Liste aller Gerätehersteller</h4>
</div>
<div class="float-right">
<a class="btn btn-primary mb-2" href="<?= self::getUrl("Devicemanufactor", "add") ?>"><i
class="fas fa-plus"></i><span
class="d-none d-lg-inline"> Neuen Gerätehersteller anlegen</span></a>
</div>
</div>
</div>
<!-- --><?php //include(realpath(dirname(__FILE__) . "/../") . "/tpl/pagination.php"); ?>
<!-- --><?php //include(realpath(dirname(__FILE__) . "/../") . "/tpl/pagination-summary.php"); ?>
<table id="datatable" class="table table-striped table-hover table-sm">
<thead>
<tr>
<th class="text-center">Name</th>
<th class="text-center">Erstellt von</th>
<th class="edit-width"></th>
</tr>
<tr id="filterrow">
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($devicemanufactors as $devicemanufactor): ?>
<tr>
<td class="text-nowrap"><?= $devicemanufactor->name ?></td>
<td class="text-nowrap"><?= $devicemanufactor->creator->name ?>
(<?= date("d.m.Y , H:i", $devicemanufactor->create) ?>)
</td>
<td style="text-align: left; letter-spacing: 4px; font-size: 1.1em;">
<a href="<?= self::getUrl("Devicemanufactor", "edit", ["id" => $devicemanufactor->id]) ?>"><i
class="far fa-edit" title="Bearbeiten"></i></a>
<a href="<?= self::getUrl("Devicemanufactor", "delete", ["id" => $devicemanufactor->id]) ?>"
onclick="if(!confirm('Gerätehersteller wirklich löschen?')) return false;"
class="text-danger" title="Löschen"><i class="fas fa-trash"></i></a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script type="text/javascript">
var hidesearch = [2];
$(document).ready(function () {
});
</script>
<script type="text/javascript" src="<?= self::getResourcePath() ?>assets/js/datatables-std.js?<?=date('U')?>"></script>
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>

View File

@@ -1,110 +0,0 @@
<?php
$pagination_baseurl = $this->getUrl($Mod, "Index");
$pagination_baseurl_params = ["filter" => $filter];
$pagination_entity_name = "Devicetype";
?>
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php"); ?>
<link href="<?= self::getResourcePath() ?>assets/css/datatables-std.css?<?= date('U') ?>" rel="stylesheet"
type="text/css"/>
<!-- start page title -->
<div class="row">
<div class="col-12">
<div class="page-title-box">
<div class="page-title-right">
<ol class="breadcrumb m-0">
<li class="breadcrumb-item"><a href="<?= self::getUrl("Dashboard") ?>"><?= MFAPPNAME_SLUG ?></a>
</li>
<li class="breadcrumb-item active">Gerätetyp</li>
</ol>
</div>
<h4 class="page-title">Gerätetyp</h4>
</div>
</div>
</div>
<!-- end page title -->
<div class="card">
<div class="card-body mb-3">
<div class="row">
<div class="col-12">
<div class="float-left">
<h4 class="header-title">Liste aller Gerätetypen</h4>
</div>
<div class="float-right">
<a class="btn btn-primary mb-2" href="<?= self::getUrl("Devicetype", "add") ?>"><i
class="fas fa-plus"></i><span
class="d-none d-lg-inline"> Neuen Gerätetyp anlegen</span></a>
</div>
</div>
</div>
<table id="datatable" class="table table-striped table-hover table-sm">
<thead>
<tr>
<th>Name</th>
<th class="text-center">Hersteller</th>
<th class="text-right">Preis</th>
<th class="text-right">max. Leistung</th>
<th class="text-right">erstellt von</th>
<th class="edit-weight"></th>
</tr>
<tr id="filterrow">
<th>Name</th>
<th>Hersteller</th>
<th>Preis</th>
<th></th>
<th></th>
<th></th>
</tr>
<thead>
<tbody>
<?php foreach ($devicetypes as $devicetype): ?>
<tr>
<td class="text-nowrap"><?= $devicetype->name ?></td>
<td class="text-center"><?= $devicetype->devicemanufactor->name ?></td>
<td class="text-right"><?= $devicetype->price ?> €</td>
<td class="text-right"><?= $devicetype->power ?> Watt</td>
<td class="text-right"><?= $devicetype->creator->name ?>
(<?= date("d.m.Y , H:i", $devicetype->create) ?>)
</td>
<td style="text-align: left; letter-spacing: 4px; font-size: 1.1em;">
<a href="<?= self::getUrl("Devicetype", "edit", ["id" => $devicetype->id]) ?>"><i
class="far fa-edit" title="Bearbeiten"></i></a>
<a href="<?= self::getUrl("Devicetype", "delete", ["id" => $devicetype->id]) ?>"
onclick="if(!confirm('Gerätetyp wirklich löschen?')) return false;" class="text-danger"
title="Löschen"><i class="fas fa-trash"></i></a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<!-- --><?php //include(realpath(dirname(__FILE__) . "/../") . "/tpl/pagination-summary.php"); ?>
<!-- --><?php //include(realpath(dirname(__FILE__) . "/../") . "/tpl/pagination.php"); ?>
<!---->
</div>
</div>
</div>
</div>
<script type="text/javascript">
var hidesearch = [5];
$(document).ready(function () {
});
</script>
<script type="text/javascript"
src="<?= self::getResourcePath() ?>assets/js/datatables-std.js?<?= date('U') ?>"></script>
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>

View File

@@ -1,101 +0,0 @@
<?php /** @noinspection PhpUndefinedClassInspection
* @var string $mfLayoutPackage
* @var TYPE_NAME $git_merge_ts
*/
$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")]
],
"DEVICE_API_URL" => self::getUrl("Device/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']['DEVICE_API_URL'] + '?do=getDevices'" :config="DeviceTableConfig" small
ref="table">
<template v-slot:top-buttons>
<button type="button" class="btn btn-primary" @click="window.alert(1)">
<i class="fas fa-plus"></i>Neues Device anlegen
</button>
</template>
<template v-slot:actions="{row}">
<a @click="window.alert(row.id)"><i class="far fa-edit text-primary" title="Bearbeiten"></i></a>
<a @click="window.alert(row.id)"><i class="fas fa-trash text-danger" title="Löschen"></i></a>
</template>
<template v-slot:power="{row}">
{{ parseInt(row.power) }} Watt
</template>
<template v-slot:price="{row}">
{{ parseInt(row.price) }} €
</template>
<template v-slot:locationText="{row}">
<a :title="row.locationText" :href="row.locationUrl" v-text="row.locationText" target="_blank"></a>
</template>
</tt-table>
</div>
<script>
new Vue({
el: '#app',
data: {
window: window,
DeviceTableConfig: {
headers: [
{text: "Device Name", key: "name"},
{text: "Geräte Typ", key: "devicetype"},
{text: "Hersteller", key: "devicemanufactor"},
{text: "Pop/Adresse", key: "locationText"},
{text: "IP-Adresse", key: "ip"},
{text: "MAC-Adresse", key: "mac"},
{text: "Seriennummer", key: "serial"},
{text: "Preis", key: "price", filter: "numberRange"},
{text: "max. Leistung", key: "power", filter: "numberRange"},
{
text: "Backup", key: "backup", filter: "iconSelect", filterOptions: [
{value: "ok", text: "Letztes Backup jünger als 48 Stunden", icon: "fa-regular fa-circle-check text-success"},
{value: "aged", text: "Letztes Backup älter als 48 Stunden", icon: "fa-regular fa-circle-xmark text-danger"},
{value: "na", text: "Kein Backup", icon: "fa-regular fa-ban text-primary"}
]},
{text: "Aktionen", key: "actions"},
],
tableHeader: 'Devices',
key: 'Device',
},
},
})
</script>
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>

View File

@@ -40,10 +40,17 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/vueHeader.ph
<div id="app">
<tt-page-title
v-if="window['TT_CONFIG'] && window['TT_CONFIG']['PAGE_TITLE'] && window['TT_CONFIG']['PATH']"
:title="window['TT_CONFIG']['PAGE_TITLE']"
:path="window['TT_CONFIG']['PATH']">
</tt-page-title>
<<?php echo $vueTagName; ?>></<?php echo $vueTagName; ?>>
</div>
<script>
const view = new Vue({el: '#app'});
const view = new Vue({el: '#app', data: {window: window}});
</script>
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>

View File

@@ -64,8 +64,6 @@
<li class="mobile-hide"><a href="<?=self::getUrl("Network")?>"><i class="fad fa-fw fa-network-wired text-info"></i> Netzgebiete</a></li>
<li class="has-sub-submenu" ><a href="<?=self::getUrl("Pop")?>"><i class="fad fa-fw fa-house text-info"></i> Pops</a></li>
<li ><a href="<?=self::getUrl("Devicemanufactor")?>"><i class="fad fa-fw fa-router text-info"></i> Geräte Hersteller</a></li>
<li><a href="<?=self::getUrl("Devicetype")?>"><i class="fad fa-fw fa-router text-info"></i> Geräte Typen</a></li>
<li class="has-sub-submenu"><a href="<?=self::getUrl("Device")?>"><i class="fad fa-fw fa-router text-info "></i> Devices</a></li>
<li class="has-sub-submenu"><a href="<?=self::getUrl("User")?>"><i class="fad fa-fw fa-users text-info"></i> Benutzer</a></li>
<li class="has-sub-submenu font-weight-bold mt-1 mobile-hide"><a>Grundstammdaten</a></li>

View File

@@ -17,21 +17,28 @@ class DeviceController extends mfBaseController
protected function indexAction()
{
$deviceManufacturers = array_map(function($manufacturer) {
$deviceManufacturers = array_map(function($deviceManufacturer) {
return [
"text" => $manufacturer->name,
"value" => $manufacturer->name,
"id" => $deviceManufacturer->id,
"name" => $deviceManufacturer->name,
"creator" => $deviceManufacturer->creator->name,
"created" => $deviceManufacturer->create,
];
}, DevicemanufactorModel::getAll());
$deviceTypes = array_map(function($deviceType) {
return [
"text" => $deviceType->name,
"value" => $deviceType->name,
"id" => $deviceType->id,
"name" => $deviceType->name,
"manufacturer" => $deviceType->devicemanufactor->name,
"price" => $deviceType->price,
"power" => $deviceType->power,
"creator" => $deviceType->creator->name,
"created" => $deviceType->create,
];
}, DevicetypeModel::getAll());
$JSGlobals = ["BASE_URL" => self::getUrl("Device"),
$JSGlobals = ["BASE_URL" => self::getUrl(""),
"DASHBOARD_URL" => self::getUrl("Dashboard"),
"MFAPPNAME" => MFAPPNAME_SLUG,
"PAGE_TITLE" => "Devices",
@@ -41,7 +48,7 @@ class DeviceController extends mfBaseController
],
"DEVICE_MANUFACTURERS" => $deviceManufacturers,
"DEVICE_TYPES" => $deviceTypes,
"DEVICE_API_URL" => self::getUrl("Device/api"),
"DEVICES" => $this->getDevices(),
];
$this->layout()->set("vueViewName", "Device");

View File

@@ -17,11 +17,7 @@ class DevicemanufactorController extends mfBaseController
protected function indexAction()
{
$this->layout()->setTemplate("Devicemanufactor/Index");
$devicemanufactors = DevicemanufactorModel::getAll();
$this->layout()->set("devicemanufactors", $devicemanufactors);
$this->redirect("Device");
}
protected function addAction()
@@ -36,17 +32,17 @@ class DevicemanufactorController extends mfBaseController
if (!is_numeric($id) || !$id) {
$this->layout()->setFlash("Gerätehersteller nicht gefunden", "error");
$this->redirect("Devicemanufactor");
$this->redirect("Device");
}
$devicemanufactors = new Devicemanufactor($id);
if ($devicemanufactors->id != $id) {
$this->layout()->setFlash("Gerätehersteller nicht gefunden", "error");
$this->redirect("Devicemanufactor");
$this->redirect("Device");
}
$this->layout()->set("devicemanufactors", $devicemanufactors);
return $this->addAction();
$this->addAction();
}
protected function saveAction()
@@ -59,7 +55,7 @@ class DevicemanufactorController extends mfBaseController
$devicemanufactor = new Devicemanufactor($id);
if (!$devicemanufactor->id) {
$this->layout()->setFlash("Gerätehersteller nicht gefunden", "error");
$this->redirect("Devicemanufactor");
$this->redirect("Device");;
}
} else {
$mode = "add";
@@ -72,7 +68,7 @@ class DevicemanufactorController extends mfBaseController
if (!$data['name']) {
$this->layout()->setFlash("Name darf nicht leer sein", "error");
$this->redirect("Devicemanufactor");
$this->redirect("Device");;
}
@@ -93,9 +89,9 @@ class DevicemanufactorController extends mfBaseController
if (!$id) {
$this->layout()->setFlash("Gerätehersteller konnte nicht angelegt werden", "error");
$this->redirect("Devicemanufactor");
$this->redirect("Device");
}
if ($fsh) {
if (isset($fsh) && $fsh) {
$fsh->save();
}
if ($mode == "edit") {
@@ -103,7 +99,7 @@ class DevicemanufactorController extends mfBaseController
} else if ($mode = "add") {
$this->layout()->setFlash("Gerätehersteller erfolgreich angelegt", "success");
}
$this->redirect("Devicemanufactor");
$this->redirect("Device");
}
protected function deleteAction()
@@ -112,11 +108,12 @@ class DevicemanufactorController extends mfBaseController
$devicemanufactor = new Devicemanufactor($id);
if (!$devicemanufactor->id || $devicemanufactor->id != $id) {
$this->layout()->setFlash("Gerätehersteller nicht gefunden.", "error");
$this->redirect("Devicemanufactor");
$this->redirect("Device");
}
$devicemanufactor->delete();
$this->redirect("Devicemanufactor");
$this->layout()->setFlash("Gerätehersteller erfolgreich gelöscht", "success");
$this->redirect("Device");
}
}

View File

@@ -18,11 +18,7 @@ class DevicetypeController extends mfBaseController
protected function indexAction()
{
$this->layout()->setTemplate("Devicetype/Index");
$devicetypes = DevicetypeModel::getAll();
$this->layout()->set("devicetypes", $devicetypes);
$this->redirect("Device");
}
protected function addAction()
@@ -38,17 +34,17 @@ class DevicetypeController extends mfBaseController
if (!is_numeric($id) || !$id) {
$this->layout()->setFlash("Gerätetyp nicht gefunden", "error");
$this->redirect("Devicetype");
$this->redirect("Device");
}
$devicetypes = new Devicetype($id);
if ($devicetypes->id != $id) {
$this->layout()->setFlash("Gerätetyp nicht gefunden", "error");
$this->redirect("Devicetype");
$this->redirect("Device");
}
$this->layout()->set("devicetypes", $devicetypes);
return $this->addAction();
$this->addAction();
}
protected function saveAction()
@@ -61,7 +57,7 @@ class DevicetypeController extends mfBaseController
$devicetype = new Devicetype($id);
if (!$devicetype->id) {
$this->layout()->setFlash("Gerätetyp nicht gefunden", "error");
$this->redirect("Devicetype");
$this->redirect("Device");
}
} else {
$mode = "add";
@@ -95,7 +91,7 @@ class DevicetypeController extends mfBaseController
if (!$data['name']) {
$this->layout()->setFlash("Name darf nicht leer sein", "error");
$this->redirect("Devicetype");
$this->redirect("Device");
}
@@ -116,7 +112,7 @@ class DevicetypeController extends mfBaseController
if (!$id) {
$this->layout()->setFlash("Gerätetyp konnte nicht angelegt werden", "error");
$this->redirect("Devicetype");
$this->redirect("Device");
}
if ($mode == "edit") {
@@ -124,7 +120,7 @@ class DevicetypeController extends mfBaseController
} else if ($mode = "add") {
$this->layout()->setFlash("Gerätetyp erfolgreich angelegt", "success");
}
$this->redirect("Devicetype");
$this->redirect("Device");
}
@@ -134,11 +130,12 @@ class DevicetypeController extends mfBaseController
$devicetype = new Devicetype($id);
if (!$devicetype->id || $devicetype->id != $id) {
$this->layout()->setFlash("Gerätetyp nicht gefunden.", "error");
$this->redirect("Devicetype");
$this->redirect("Device");
}
$devicetype->delete();
$this->redirect("Devicetype");
$this->layout()->setFlash("Gerätetyp erfolgreich gelöscht", "success");
$this->redirect("Device");
}
}

View File

@@ -1,5 +1,9 @@
/* Overrides */
html {
overflow-y: scroll;
}
body {
color: #323a36;
background-color: #e7e7e7 !important;
@@ -161,6 +165,7 @@ h1, h2, h3, h4, h5, h6 {
/* allow icons in custom-file label */
.custom-file-label::before {
/*noinspection CssNoGenericFontName*/
font-family: "Font Awesome 6 Pro";
content: "\f56f";
padding-right: 6px;

View File

@@ -30,6 +30,7 @@ $jsFiles = [
"plugins/moment/moment.min.js",
"plugins/daterangepicker/daterangepicker.js",
"plugins/vue/" . (isset($_GET['VUE_DEBUG']) || $_SERVER['HTTP_HOST'] === "localhost" ? "vue.js" : "vue.min.js"),
"plugins/vue/tt-components/tt-card.js",
"plugins/vue/tt-components/tt-table.js",
"plugins/vue/tt-components/tt-page-title.js",
"plugins/vue/tt-components/tt-loader.js",

View File

@@ -1,24 +1,70 @@
Vue.component('Device', {
const deviceManufacturerFilterOptions = window?.TT_CONFIG?.DEVICE_MANUFACTURERS.map(manufacturer => ({
value: manufacturer.name,
text: manufacturer.name,
}));
const deviceTypeFilterOptions = window?.TT_CONFIG?.DEVICE_TYPES.map(type => ({
value: type.name,
text: type.name,
}));
Vue.component('device-view-switch', {
//language=Vue
template: `
<div>
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
<div class="device-view-switch" style="margin-bottom: 10px">
<div v-if="!isOverflowing" class="button-group" style="display:grid; grid-template-columns: repeat(3, 1fr); gap: 10px; justify-content: center; align-items: center; text-align: center; width: 100%;">
<button @click="$emit('input', 'DeviceTable')" :class="{ 'active': value === 'DeviceTable' }" class="btn btn-primary">Devices</button>
<button @click="$emit('input', 'DeviceManufacturer')" :class="{ 'active': value === 'DeviceManufacturer' }" class="btn btn-primary">Hersteller</button>
<button @click="$emit('input', 'DeviceType')" :class="{ 'active': value === 'DeviceType' }" class="btn btn-primary">Geräte Typen</button>
</div>
<div v-else>
<div class="dropdown">
<button @click="showDropdown = !showDropdown"
class="btn btn-primary dropdown-toggle">Ansicht</button>
<div v-show="showDropdown" class="dropdown-menu show">
<a href="#" @click="$emit('input', 'DeviceTable'); showDropdown = false" class="dropdown-item">Devices</a>
<a href="#" @click="$emit('input', 'DeviceManufacturer'); showDropdown = false" class="dropdown-item">Hersteller</a>
<a href="#" @click="$emit('input', 'DeviceType'); showDropdown = false" class="dropdown-item">Geräte Typen</a>
</div>
</div>
</div>
</div>
`,
props: ['value'],
data() {
return {
isOverflowing: false,
showDropdown: false,
};
},
mounted() {
this.checkOverflow();
window.addEventListener('resize', this.checkOverflow);
},
beforeDestroy() {
window.removeEventListener('resize', this.checkOverflow);
},
methods: {
checkOverflow() {
this.isOverflowing = window.innerWidth < 650
},
},
})
<tt-table
:fetch-url="window['TT_CONFIG']['DEVICE_API_URL'] + '?do=getDevices'"
:config="DeviceTableConfig"
small ref="table" excel-export
>
Vue.component('DeviceTable', {
//language=Vue
template: `
<tt-table :data="window['TT_CONFIG']['DEVICES']" :config="DeviceTableConfig" excel-export>
<template v-slot:top-buttons>
<button type="button" class="btn btn-primary" @click="window.location = window['TT_CONFIG']['BASE_URL'] + '/add'">
<button type="button" class="btn btn-primary" @click="window.location = window['TT_CONFIG']['BASE_URL'] + '/Device/add'">
<i class="fas fa-plus"></i>
Device hinzufügen
</button>
</template>
<template v-slot:name="{ row }">
<a target="_blank" :href="window['TT_CONFIG']['BASE_URL'] +'/Detail?id=' + row.id">{{row.name}}</a>
<a target="_blank" :href="window['TT_CONFIG']['BASE_URL'] +'/Device/Detail?id=' + row.id">{{row.name}}</a>
</template>
<template v-slot:locationtext="{ row }">
@@ -26,14 +72,12 @@ Vue.component('Device', {
</template>
<template v-slot:actions="{ row }">
<a :href="window['TT_CONFIG']['BASE_URL'] +'/edit/?id=' + row.id"><i class="far fa-edit" title="Bearbeiten"></i></a>
<a :href="window['TT_CONFIG']['BASE_URL'] +'/delete/?id=' + row.id" onclick="if(!confirm('Device wirklich löschen?')) return false;" class="text-danger" title="Löschen"><i class="fas fa-trash "></i></a>
<a :href="window['TT_CONFIG']['BASE_URL'] +'/Device/edit/?id=' + row.id"><i class="far fa-edit" title="Bearbeiten"></i></a>
<a :href="window['TT_CONFIG']['BASE_URL'] +'/Device/delete/?id=' + row.id" onclick="if(!confirm('Device wirklich löschen?')) return false;" class="text-danger" title="Löschen"><i class="fas fa-trash "></i></a>
</template>
</tt-table>
</div>
`,
data() {
return {
window: window,
@@ -43,8 +87,8 @@ Vue.component('Device', {
defaultPageSize: 25,
headers: [
{ text: 'Device Name', key: 'name', sortable: true, class: 'text-nowrap', priority: 10 },
{ text: 'Hersteller', key: 'devicemanufactor', filter: 'select',class: 'text-nowrap text-center', filterOptions: window.TT_CONFIG["DEVICE_MANUFACTURERS"] },
{ text: 'Geräte Typ', key: 'devicetype', filter: 'autocomplete', class: 'text-nowrap text-center', filterOptions: window.TT_CONFIG["DEVICE_TYPES"] , priority: 7},
{ text: 'Hersteller', key: 'devicemanufactor', filter: 'select',class: 'text-nowrap text-center', filterOptions: deviceManufacturerFilterOptions },
{ text: 'Geräte Typ', key: 'devicetype', filter: 'autocomplete', class: 'text-nowrap text-center', filterOptions: deviceTypeFilterOptions , priority: 7},
{ text: 'Pop/Adresse', key: 'locationText', filter: 'search' , class: 'text-nowrap text-center'},
{ text: 'IP-Adresse', key: 'ip', filter: 'search',class: 'text-center', priority: 8},
{ text: 'Mac-Adresse', key: 'mac', filter: 'search',class: 'text-center' },
@@ -67,6 +111,109 @@ Vue.component('Device', {
{text: 'Aktionen', key: 'actions', class: 'text-center', sortable: false, filter: false, priority: 9},
],
},
}
}
});
Vue.component('DeviceManufacturer', {
//language=Vue
template: `
<tt-table :data="window['TT_CONFIG']['DEVICE_MANUFACTURERS']" :config="DeviceManufacturerConfig" excel-export>
<template v-slot:top-buttons>
<button type="button" class="btn btn-primary" @click="window.location = window['TT_CONFIG']['BASE_URL'] + '/Devicemanufactor/add'">
<i class="fas fa-plus"></i>Hersteller hinzufügen
</button>
</template>
<template v-slot:actions="{ row }">
<a :href="window['TT_CONFIG']['BASE_URL'] +'/Devicemanufactor/edit/?id=' + row.id"><i class="far fa-edit" title="Bearbeiten"></i></a>
<a :href="window['TT_CONFIG']['BASE_URL'] +'/Devicemanufactor/delete/?id=' + row.id" onclick="if(!confirm('Hersteller wirklich löschen?')) return false;" class="text-danger" title="Löschen"><i class="fas fa-trash "></i></a>
</template>
</tt-table>
`,
data() {
return {
window: window,
DeviceManufacturerConfig: {
key: 'DeviceManufacturer',
tableHeader: 'Hersteller',
defaultPageSize: 25,
headers: [
{ text: 'Name', key: 'name', sortable: true, class: 'text-nowrap', priority: 10 },
{ text: 'Erstellungsdatum', key: 'created', filter: 'date', class: 'text-center'},
{ text: 'Erstellt von', key: 'creator', filter: 'search', class: 'text-center'},
{ text: 'Aktionen', key: 'actions', class: 'text-center', sortable: false, filter: false, priority: 9},
],
},
}
}
});
Vue.component('DeviceType', {
//language=Vue
template: `
<tt-table :data="window['TT_CONFIG']['DEVICE_TYPES']" :config="DeviceTypeConfig" excel-export>
<template v-slot:top-buttons>
<button type="button" class="btn btn-primary" @click="window.location = window['TT_CONFIG']['BASE_URL'] + '/Devicetype/add'">
<i class="fas fa-plus"></i>Device Type hinzufügen
</button>
</template>
<template v-slot:actions="{ row }">
<a :href="window['TT_CONFIG']['BASE_URL'] +'/Devicetype/edit/?id=' + row.id"><i class="far fa-edit" title="Bearbeiten"></i></a>
<a :href="window['TT_CONFIG']['BASE_URL'] +'/Devicetype/delete/?id=' + row.id" onclick="if(!confirm('Gerätetyp wirklich löschen?')) return false;" class="text-danger" title="Löschen"><i class="fas fa-trash "></i></a>
</template>
</tt-table>
`,
data() {
return {
window: window,
DeviceTypeConfig: {
key: 'DeviceType',
tableHeader: 'Gerätetypen',
defaultPageSize: 25,
headers: [
{ text: 'Name', key: 'name', sortable: true, class: 'text-nowrap', priority: 10 },
{ text: 'Hersteller', key: 'manufacturer', filter: 'search', class: 'text-center'},
{ text: 'Preis', key: 'price', filter: 'numberRange', class: 'text-center', suffix: ' €' },
{ text: 'max. Leistung', key: 'power', filter: 'numberRange', class: 'text-center', suffix: ' W' },
{ text: 'Erstellungsdatum', key: 'created', filter: 'date', class: 'text-center'},
{ text: 'Erstellt von', key: 'creator', filter: 'search', class: 'text-center'},
{ text: 'Aktionen', key: 'actions', class: 'text-center', sortable: false, filter: false, priority: 9},
],
},
}
}
})
Vue.component('Device', {
//language=Vue
template: `
<tt-card>
<device-view-switch v-model="currentView"></device-view-switch>
<component :is="currentView"></component>
</tt-card>
`,
data() {
return {
window: window,
currentView: 'DeviceTable',
};
},
beforeMount() {
const storedView = window.localStorage.getItem('tt-device-view');
if (storedView) {
this.currentView = storedView;
}
},
watch: {
currentView() {
window.localStorage.setItem('tt-device-view', this.currentView);
}
},
});

View File

@@ -1,10 +1,7 @@
Vue.component('Domain', {
//language=Vue
template: `
<div>
<!-- start page title -->
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
<tt-card>
<tt-table :fetch-url="window['TT_CONFIG']['DOMAIN_API_URL'] + '?do=getDomains'" :config="domainsTableConfig"
small ssr ref="table">
@@ -99,7 +96,7 @@ Vue.component('Domain', {
</div>
</div>
</div>
</tt-card>
`,
data() {
return {
@@ -119,7 +116,7 @@ Vue.component('Domain', {
}, computed: {
domainsTableConfig() {
const base = {
headers: [{text: "DNS", key: "inwxRoId", filter: false, sortable: false}, {
headers: [{
text: "Domain",
key: "domain"
}, {
@@ -157,7 +154,8 @@ Vue.component('Domain', {
text: "Tech ID",
key: "tech",
sortable: false
}, {text: "Billing ID", key: "billing", sortable: false}, {text: "Name Servers", key: "ns"}],
}, {text: "Billing ID", key: "billing", sortable: false}, {text: "Name Servers", key: "ns"},
{text: "DNS", key: "inwxRoId", filter: false, sortable: false, priority: 1}],
tableHeader: 'Domains',
key: 'Domain'
}

View File

@@ -1,8 +1,7 @@
Vue.component('HistoricTicket', {
//language=Vue
template: `
<div>
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
<tt-card>
<tt-table :fetch-url="window['TT_CONFIG']['HISTORIC_TICKET_API_URL'] + '?do=getHistoricTickets'"
:config="historicTicketTableConfig"
@@ -100,7 +99,7 @@ Vue.component('HistoricTicket', {
</div>
</div>
</div>
</tt-card>
`, data() {
return {
window: window,

View File

@@ -1,9 +1,7 @@
Vue.component('IpNetwork', {
//language=Vue
template: `
<div>
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']"
:path="window['TT_CONFIG']['PATH']"></tt-page-title>
<tt-card>
<tt-table :fetch-url="window['TT_CONFIG']['IPNETWORK_API_URL'] + '?do=get'"
:config="IpNetworkTableConfig"
@@ -90,7 +88,7 @@ Vue.component('IpNetwork', {
</div>
</div>
</div>
</div>
</tt-card>
`,
data() {
return {

View File

@@ -1,8 +1,7 @@
Vue.component('VoiceCallActive', {
//language=Vue
template: `
<div>
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
<tt-card>
<tt-table :fetch-url="window['TT_CONFIG']['VOICE_CALL_ACTIVE_API_URL'] + '?do=getActiveCalls'"
:config="VoiceCallActiveTableConfig"
@@ -47,7 +46,7 @@ Vue.component('VoiceCallActive', {
</tt-table>
</div>
</tt-card>
`,
data() {
return {

View File

@@ -1,8 +1,7 @@
Vue.component('VoiceCallHistory', {
//language=Vue
template: `
<div>
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
<tt-card>
<tt-table :fetch-url="window['TT_CONFIG']['VOICE_CALL_HISTORY_API_URL'] + '?do=getCalls'"
:config="VoiceCallHistoryTableConfig"
@@ -20,7 +19,7 @@ Vue.component('VoiceCallHistory', {
</template>
</tt-table>
</div>
</tt-card>
`, data() {
return {
window: window,

View File

@@ -21,11 +21,7 @@
}
.tt-table-card {
/*overflow: auto;*/
}
.tt-table-card .page-link {
.tt-table-container .page-link {
padding: 5px .75rem !important;
}
@@ -112,12 +108,24 @@ input[type=number]::-webkit-outer-spin-button {
.tt-table-top-pagination-container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-columns: auto auto;
padding-bottom: 8px;
align-items: center;
justify-content: space-between
}
.tt-table-top-buttons-container {
display: grid;
grid-template-columns: auto auto auto auto auto;
grid-gap: 8px;
padding-bottom: 8px
}
.tt-table-top-buttons-container > button > i {
font-size: 17px;
margin-right: 4px;
}
@media (max-width: 486px) {
.tt-table-top-pagination-container {
grid-template-columns: 1fr;

View File

@@ -72,8 +72,6 @@ Vue.component('tt-autocomplete', {
this.displayValue = selectedItem ? selectedItem.text : '';
}
console.log(this.$slots.append ? 'append' : 'no append');
},
data() {
return {

View File

@@ -0,0 +1,16 @@
Vue.component('tt-card', {
//language=Vue
template: `
<div class="card">
<div class="card-header" v-if="$slots.header">
<slot name="header"></slot>
</div>
<div class="card-body">
<slot></slot>
</div>
<div class="card-footer" v-if="$slots.footer">
<slot name="footer"></slot>
</div>
</div>
`,
})

View File

@@ -99,5 +99,6 @@ Vue.component('tt-date-picker', {
},
beforeDestroy() {
$(this.$refs.input).off('apply.daterangepicker');
$('.daterangepicker').remove();
},
})

View File

@@ -109,11 +109,9 @@ Vue.component('tt-table-pagination', {
Vue.component('tt-table', {
template: `
<div class="card tt-table-card" v-if="columns && pagination">
<div class="card-body" ref="tableContainer">
<div class="tt-table-container" ref="tableContainer">
<!-- Top Buttons -->
<div
style="display:grid; grid-template-columns: auto auto auto auto auto; grid-gap: 8px; padding-bottom: 8px">
<div class="tt-table-top-buttons-container">
<slot name="top-buttons"></slot>
</div>
<!-- Pagination Controls -->
@@ -142,7 +140,10 @@ Vue.component('tt-table', {
<th scope="col" v-for="column in columns"
:ref="'table_header_'+column.key"
v-if="!hiddenColumns.includes(column.key)"
:style="'vertical-align: top; text-align: center;' + (column.filter === 'dateRange' ? 'min-width: 260px;' : '')">
:style="'vertical-align: top; text-align: center;' +
(column.filter === 'dateRange' ? 'min-width: 260px;' : '') +
(originalColumnWidths[column.key] ? 'width: ' + originalColumnWidths[column.key] + 'px;' : '')"
>
<div style="text-align:center; white-space: nowrap;word-break: keep-all;"
:style="{ 'cursor': column.sortable ? 'pointer' : 'default' }"
@click="column.sortable ? setOrder(column.key) : undefined">
@@ -180,7 +181,8 @@ Vue.component('tt-table', {
@click="$emit('row-click', row)"
>
<template v-for="(column, key) in columns" v-if="!hiddenColumns.includes(column.key)">
<td :class="{ 'text-center': column.filter === 'iconSelect', [columns[key].class]: true }">
<td :class="{ 'text-center': column.filter === 'iconSelect',
[columns[key].class]: true, 'text-nowrap': !originalColumnWidths[column.key] }">
<!-- If td is first of row then check isExpanded and display fas.fa-chevron-right or fas.fa-chevron-down with cursor pointer -->
<i v-if="key === Object.keys(columns)[0] &&
($scopedSlots.expandedRow && (typeof config.expandCondition !== 'function' || config.expandCondition(row)) || hiddenColumns.length > 0)"
@@ -236,9 +238,9 @@ Vue.component('tt-table', {
v-if="pagination"></tt-table-pagination>
</nav>
</div>
</div>
`, props: {
fetchUrl: String,
data: Array,
striped: {type: Boolean, default: true},
bordered: {type: Boolean, default: true},
hover: {type: Boolean, default: true},
@@ -293,13 +295,17 @@ Vue.component('tt-table', {
* @async
*/
async fetchData(page = 0) {
console.log('Fetching data...');
this.expandedRows = {};
try {
if (this.ssr === false) {
if (this.data) {
this.rawRows = this.data;
} else {
const response = await axios.get(this.fetchUrl);
this.rawRows = response.data
}
this.pagination = {
page: page++,
@@ -673,7 +679,8 @@ Vue.component('tt-table', {
} else if (header.filter === 'date') {
if (!filterValue.from || !filterValue.to) continue;
let rowDate = new Date(row[header.key]).getTime() / 1000;
const dateInt = row[header.key].length === 10 ? parseInt(row[header.key]) * 1000 : parseInt(row[header.key]);
let rowDate = new Date(dateInt).getTime() / 1000;
if (rowDate < filterValue.from || rowDate > filterValue.to) {
match = false;
@@ -698,8 +705,13 @@ Vue.component('tt-table', {
output.sort((a, b) => {
let valueA = isDateColumn ? new Date(a[this.order.key]).getTime() : a[this.order.key];
let valueB = isDateColumn ? new Date(b[this.order.key]).getTime() : b[this.order.key];
let valueA = isDateColumn ?
new Date(a[this.order.key].length === 10 ? parseInt(a[this.order.key]) * 1000 : parseInt(a[this.order.key])).getTime() :
a[this.order.key];
let valueB = isDateColumn ?
new Date(b[this.order.key].length === 10 ? parseInt(b[this.order.key]) * 1000 : parseInt(b[this.order.key])).getTime() :
b[this.order.key];
if (valueA === valueB) return 0;