Merge branch 'spidev' into 'master'

Faserplanung

See merge request fronk/thetool!1917
This commit is contained in:
Daniel Spitzer
2025-12-02 11:50:45 +00:00
12 changed files with 7531 additions and 681 deletions
+104 -271
View File
@@ -1,274 +1,11 @@
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php"); ?>
<link href="<?= self::getResourcePath() ?>assets/css/datatables-std.css?<?= $git_merge_ts ?>" rel="stylesheet"
type="text/css"/>
<style>
.card-border {
border-left: 1px solid #428bca;
border-left-width: 5px;
border-radius: 3px;
}
.font-weight-500 {
font-weight: 500;
}
.order-date-pill {
margin: 2px;
white-space: nowrap;
}
.rack-he:hover {
box-shadow: 2px 2px 5px 2px rgba(0, 0, 0, 0.31);
}
.rack-color-lwl {
background: rgb(159, 255, 96);
background: linear-gradient(180deg, rgb(129 223 68) 0%, rgb(62 155 3) 100%);
color: #000000;
padding-top: 2px;
}
.rack-color-device {
background: rgb(182, 198, 240);
background: linear-gradient(180deg, rgba(182, 198, 240, 1) 0%, rgb(131 158 255 / 82%) 100%);
font-weight: 500;
padding-top: 2px;
}
.rack-color-infra {
background: rgb(251 181 109);
background: linear-gradient(180deg, rgb(247 184 118) 0%, rgb(255 149 40) 100%);
font-weight: 500;
padding-top: 2px;
}
.rack-color-panel {
background: rgb(150, 243, 222);
background: linear-gradient(180deg, rgba(150, 243, 222, 1) 0%, rgba(87, 249, 212, 1) 100%);
font-weight: 500;
padding-top: 2px;
}
.rack-color-blocked {
background: rgb(255, 0, 0);
background: linear-gradient(180deg, rgba(255, 0, 0, 1) 0%, rgba(196, 0, 0, 1) 100%);
font-weight: 500;
padding-top: 2px;
color: #fff;
}
.rack-color-rpanel {
background: rgb(88, 201, 240);
background: linear-gradient(180deg, rgba(88, 201, 240, 1) 0%, rgba(64, 188, 231, 1) 100%);
font-weight: 500;
padding-top: 2px;
}
.user-select-none {
user-select: none;
}
.fa-edit {
margin-right: 5px;
margin-top: 2px;
cursor: pointer;
}
.rack-name {
}
.move-handle {
cursor: grab;
margin-left: 5px;
margin-top: 2px;
}
.move-handle:active {
cursor: grabbing;
}
.border-right {
border-right-color: #dee2e6 !important;
}
.form-control:disabled, .form-control[readonly] {
background-color: #d7d7d7;
opacity: 1;
}
.switch-rack-side {
margin-right: 8px;
margin-top: 2px;
cursor: pointer;
}
.cable-container {
display: flex;
width: 100%;
height: 100%;
align-items: stretch;
color: #000;
cursor: pointer;
}
.cable-item {
border-right: 1px solid #797979;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 12px;
line-height: 1.1;
color: #333;
background-color: #e1ffdf;
cursor: pointer;
overflow: hidden;
white-space: nowrap;
padding: 1px 0;
position: relative;
}
.cable-item .port-range, .cable-item .fiber-range {
font-size: 12px;
color: #040404;
font-weight: 500;
}
.cable-item:hover {
background-color: #cfffc8;
}
.cable-item:last-child {
border-right: none;
}
.cable-free {
flex-grow: 1;
}
.context-menu {
display: none;
position: absolute;
z-index: 1000;
width: 180px;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.15);
}
.context-menu ul {
list-style: none;
padding: 5px 0;
margin: 0;
}
.context-menu ul li {
padding: 8px 15px;
cursor: pointer;
font-size: 14px;
}
.context-menu ul li:hover {
background-color: #f0f0f0;
}
.context-menu ul li i {
margin-right: 10px;
color: #555;
}
#cable-context-menu {
position: absolute; /* Popper setzt top/left */
z-index: 20000;
transform: none !important; /* falls doch mal ein transform reingeschummelt wird */
}
.dropdown-menu[x-placement^=left], .dropdown-menu[x-placement^=right], .dropdown-menu[x-placement^=top] {
top: unset;
-webkit-animation: none !important;
animation: none !important;
}
.context-span {
min-width: 25px;
display: inline-block;
}
.dropdown-item {
padding: .3rem 1.2rem;
}
.w-10 {
width: 10% !important;
}
.cable-highlight {
background-color: #e6ff00 !important;
border: 2px solid #e0a800 !important;
color: #000;
}
/* NEU: Animation für das "Aufblinken" */
.cable-flash {
/*animation: flash-animation 1s;*/
}
@keyframes flash-animation {
0% {
transform: scale(1);
}
25% {
transform: scale(1.05);
}
50% {
transform: scale(1);
}
75% {
transform: scale(1.05);
}
100% {
transform: scale(1);
}
}
/* Sorgt dafür, dass die Autocomplete-Vorschläge über anderen Elementen liegen */
.ui-autocomplete {
z-index: 2000;
}
.module-highlight {
/*box-shadow: inset 0 0 0 3px #007bff !important; !* Ein auffälliger, blauer Innen-Rahmen *!*/
/*transition: box-shadow 0.3s ease-in-out;*/
}
.rack-color-lwl-planned {
background: rgb(255, 0, 0);
background: linear-gradient(180deg, rgba(255, 0, 0, 1) 0%, rgba(196, 0, 0, 1) 100%);
font-weight: 500;
padding-top: 2px;
color: #fff;
}
.port-info {
font-size: 12px;
font-weight: 500;
}
.cable-description
{
font-size: 12px;
font-style: italic;
color: #000000;
white-space: normal;
line-height: 1.2;
font-weight: 400;
}
</style>
<link href="<?= self::getResourcePath() ?>assets/css/print.min.css?<?= $git_merge_ts ?>" rel="stylesheet"
type="text/css"/>
<!--<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.css">-->
<link href="<?= self::getResourcePath() ?>css/pages/Pop/Detail.css?<?= $git_merge_ts ?>" rel="stylesheet"
type="text/css"/>
<div class="row">
<div class="col-12">
<div class="page-title-box">
@@ -692,6 +429,10 @@ if (!empty(trim($pops->vlan_ipv6)))
class="rack-name"><i
class="fa-regular fa-arrows-up-down-left-right move-handle float-left"></i><?= $poprack['rack']['name']; ?>&nbsp;<span
class="rack-side-indicator font-weight-normal">-&nbsp;Vorderseite</span></span>
<i class="fas fa-expand float-right mr-2 rack-fullscreen-btn"
title="Vollbild & Drucken"
style="cursor: pointer; margin-top: 2px;"
data-rackid="<?= $poprack['rack']['id']; ?>"></i>
<i class="fas fa-sync-alt float-right switch-rack-side"
title="Seite wechseln"></i>
@@ -779,7 +520,8 @@ if (!empty(trim($pops->vlan_ipv6)))
<div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="cable-description">Beschreibung</label>
<div class="col-lg-6">
<textarea id="cable-description" name="cable-description" class="form-control" rows="3"></textarea>
<textarea id="cable-description" name="cable-description" class="form-control"
rows="3"></textarea>
</div>
</div>
<div class="alert alert-danger text-center" role="alert" style="display: none"
@@ -849,7 +591,8 @@ if (!empty(trim($pops->vlan_ipv6)))
<div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="cable-description">Beschreibung</label>
<div class="col-lg-6">
<textarea id="edit-cable-description" name="cable-description" class="form-control" rows="3"></textarea>
<textarea id="edit-cable-description" name="cable-description" class="form-control"
rows="3"></textarea>
</div>
</div>
<div class="alert alert-danger text-center" role="alert" style="display: none"
@@ -864,6 +607,80 @@ if (!empty(trim($pops->vlan_ipv6)))
</div>
</div>
</div>
<div class="modal fade" id="fiberPlanCableModal" tabindex="-1" role="dialog" aria-labelledby="fiberPlanCableModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="fiberPlanCableModalLabel">
<i class="fa fa-cable"></i> Kabel-Details
<!-- <button class="btn btn-primary btn-sm ml-3"-->
<!-- id="modal-edit-cable-btn"-->
<!-- style="display: none;">-->
<!-- <i class="fas fa-table"></i> Excel-Editor-->
<!-- </button>-->
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" id="fiberPlanCableModalBody">
<div class="text-center py-5">
<div class="spinner-border text-primary" role="status"></div>
<p class="mt-3">Lade Kabel-Informationen...</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Schließen</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="excelEditorModal" tabindex="-1" role="dialog" style="display: none;">
<div class="modal-dialog modal-fullscreen-custom" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="fas fa-table"></i> Kabel bearbeiten: <span id="cable-name"></span>
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body p-0 d-flex flex-column" style="flex: 1; overflow: hidden;">
<div id="handsontable-container" style="flex: 1; width: 100%; overflow: hidden;"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
<i class="fas fa-times"></i> Abbrechen
</button>
<button type="button" class="btn btn-success" id="save-excel-data">
<i class="fas fa-save"></i> Speichern
</button>
</div>
</div>
</div>
</div>
<div id="customRackOverlay">
<div class="overlay-header">
<h3 id="overlayTitle">Rack Detail</h3>
<div>
<button class="btn btn-info mr-2" onclick="printRackOverlay('landscape');" title="Querformat drucken">
<i class="fas fa-print"></i> <i class="fas fa-arrows-alt-h"></i> Quer
</button>
<button class="btn btn-info mr-2" onclick="printRackOverlay('portrait');" title="Hochformat drucken">
<i class="fas fa-print"></i> <i class="fas fa-arrows-alt-v"></i> Hoch
</button>
<button class="btn btn-secondary" onclick="$('#customRackOverlay').fadeOut(); $('body').css('overflow', '');">
<i class="fas fa-times"></i> Schließen
</button>
</div>
</div>
<div id="overlayContent" class="d-flex justify-content-center overlayContent">
</div>
</div>
<div id="cable-context-menu" class="dropdown-menu" style="display:none;">
<a class="dropdown-item" href="#" data-action="view"><span class="context-span"><i
class="fas fa-eye fa-fw"></i></span> Ansehen</a>
@@ -889,10 +706,26 @@ if (!empty(trim($pops->vlan_ipv6)))
let linkAddCable = "<?= self::getUrl("Poprackmodulecable", "api", ['do' => 'addCable']) ?>";
let linkRemoveCable = "<?= self::getUrl("Poprackmodulecable", "api", ['do' => 'removeCable']) ?>";
let linkUpdateCable = "<?= self::getUrl("Poprackmodulecable", "api", ['do' => 'updateCable']) ?>";
let linkGetCableDetails = "<?= self::getUrl('Pop', 'api', ['do' => 'getCableDetails']) ?>";
let linkGetFiberPath = "<?= self::getUrl('Pop', 'api', ['do' => 'getFiberPath']) ?>";
let linkGetCableFibersForEdit = "<?php echo mfBaseController::getUrl('Pop', 'api', array('do' => 'getCableFibersForEdit')); ?>";
let linkSaveCableFibers = "<?php echo mfBaseController::getUrl('Pop', 'api', array('do' => 'saveCableFibers')); ?>";
L.MakiMarkers.accessToken = '<?=TT_MAPBOX_TILE_API_TOKEN?>';
var allCables = <?php echo $cables_json ?? '[]'; ?>;
var popNetworkIds = <?php echo $popnetwork_ids ?? '[]'; ?>;
$(function () {
$('[data-toggle="popover"]').popover('dispose');
$('[data-toggle="popover"]').popover();
});
</script>
<!--<script src="https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.js"></script>-->
<script type="text/javascript" src="<?= self::getResourcePath() ?>assets/js/print.min.js?<?= $git_merge_ts ?>"></script>
<script type="text/javascript" src="<?= self::getResourcePath() ?>js/pages/pop/detail.js?<?= $git_merge_ts ?>"></script>
<script type="text/javascript" src="<?= self::getResourcePath() ?>js/pages/pop/fiber.js?<?= $git_merge_ts ?>"></script>
<!--<script type="text/javascript"-->
<!-- src="--><?php //= self::getResourcePath() ?><!--js/pages/pop/fibertable.js?--><?php //= $git_merge_ts ?><!--"></script>-->
<script type="text/javascript"
src="<?= self::getResourcePath() ?>assets/js/datatables-std.js?<?= $git_merge_ts ?>"></script>
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>
+37 -9
View File
@@ -54,25 +54,53 @@ for ($i = 1; $i <= $rack_he; $i++) : ?>
}
$portCount = $cable['port_end'] - $cable['port_start'] + 1;
$cableWidth = ($portCount / $slots['ports']) * 100;
$cableTitle = htmlspecialchars($cable['cable_name'] . ' (Ports ' . $cable['port_start'] . '-' . $cable['port_end'] . ')', ENT_QUOTES);
if (!empty($cable['description'])) {
$cableTitle .= ' | ' . htmlspecialchars($cable['description'], ENT_QUOTES);
$popTitle = htmlspecialchars($cable['cable_name'], ENT_QUOTES);
$popContent = '<strong>Ports:</strong> ' . $cable['port_start'] . '-' . $cable['port_end'];
if (!empty($cable['fiber_start'])) {
$popContent .= '<br><strong>Fasern:</strong> ' . $cable['fiber_start'] . '-' . $cable['fiber_end'];
}
$cellContent .= '<div class="cable-item" title="' . $cableTitle . '" style="width: ' . $cableWidth . '%;" data-cable-id="' . $cable['id'] . '"
if (!empty($cable['description'])) {
$popContent .= '<br><div class="mt-1 text-muted border-top pt-1 small"><em>' . htmlspecialchars($cable['description'], ENT_QUOTES) . '</em></div>';
}
$narrowClass = ($cableWidth < 12) ? 'narrow' : '';
$cellContent .= '<div class="cable-item ' . $narrowClass . '"
data-toggle="popover"
data-trigger="hover"
data-html="true"
data-placement="top"
data-container="body"
title="' . $popTitle . '"
data-content="' . htmlspecialchars($popContent, ENT_QUOTES) . '"
style="width: ' . $cableWidth . '%;"
data-cable-id="' . $cable['id'] . '"
data-cable-name="' . htmlspecialchars($cable['cable_name'], ENT_QUOTES) . '"
data-port-start="' . $cable['port_start'] . '"
data-port-end="' . $cable['port_end'] . '"
data-fiber-start="' . ($cable['fiber_start'] ?? '') . '"
data-fiber-end="' . ($cable['fiber_end'] ?? '') . '"
data-description="' . htmlspecialchars($cable['description'] ?? '', ENT_QUOTES) . '">';
$cellContent .= '<span><b>' . $cable['cable_name'] . "</b></span>";
$fontSize = ($cableWidth < 10) ? '10px' : 'inherit';
$cellContent .= '<span style="font-size:'.$fontSize.'; overflow:hidden; text-overflow:ellipsis; max-width:100%; white-space:nowrap;"><b>' . $cable['cable_name'] . "</b></span>";
if (!empty($cable['description'])) {
$cellContent .= '<span class="cable-description" >' . htmlspecialchars($cable['description'], ENT_QUOTES) . '</span>';
$descVisibilityClass = ($cableWidth <= 20) ? 'hide-if-narrow' : '';
$cellContent .= '<span class="cable-description ' . $descVisibilityClass . '" >' . htmlspecialchars($cable['description'], ENT_QUOTES) . '</span>';
}
$cellContent .= '<div class="port-ranges-container" style="display: flex; justify-content: center; width: 100%; font-size: 10px; gap: 8px;">';
$cellContent .= '<tool class="port-range">P: ' . $cable['port_start'] . '-' . $cable['port_end'] . '</tool>';
$rangesVisibilityClass = ($cableWidth <= 5) ? 'hide-if-narrow' : '';
$infoSize = '10px';
$infoGap = '8px';
if ($cableWidth < 8) { $infoSize = '8px'; $infoGap = '1px'; }
elseif ($cableWidth < 15) { $infoSize = '9px'; $infoGap = '3px'; }
$toolStyle = 'font-size: ' . $infoSize . '; white-space: nowrap;';
$cellContent .= '<div class="port-ranges-container ' . $rangesVisibilityClass . '" style="display: flex; justify-content: center; width: 100%; line-height: 1.1; gap: ' . $infoGap . ';">';
$cellContent .= '<tool class="port-range" style="' . $toolStyle . '">P: ' . $cable['port_start'] . '-' . $cable['port_end'] . '</tool>';
if (!empty($cable['fiber_start'])) {
$cellContent .= '<tool class="fiber-range">F: ' . $cable['fiber_start'] . '-' . $cable['fiber_end'] . "</tool>";
$cellContent .= '<tool class="fiber-range" style="' . $toolStyle . '">F: ' . $cable['fiber_start'] . '-' . $cable['fiber_end'] . "</tool>";
}
$cellContent .= '</div>';
@@ -25,7 +25,11 @@ class FiberPlanCableModel
private $address_id;
private $network_id;
private $comment;
private $parent_cable_id;
private $is_branch_cable;
private $branch_from_location;
private $branch_to_location;
private $coordinates;
public static $fibersArray = array(1 => 4, 2 => 12, 3 => 24, 4 => 24, 5 => 48, 6 => 96, 7 => 144, 8 => 144, 9 => 192, 10 => 288);
public static function find($data)
@@ -132,12 +136,26 @@ class FiberPlanCableModel
private static function getSqlFilter($filter)
{
$where = "1=1 ";
if (array_key_exists("description", $filter) && $filter['description']) {
$description = $filter['description'];
$where .= " AND description='$description'";
}
//var_dump($filter);exit;
if (array_key_exists("network_id", $filter)) {
$networkid = $filter['network_id'];
if (is_numeric($networkid)) {
$where .= " AND network_id=$networkid";
if (is_array($filter['network_id'])) {
$ids = array_map('intval', $filter['network_id']);
$ids = array_filter($ids, function($id) { return $id > 0; });
if (!empty($ids)) {
$idList = implode(',', $ids);
$where .= " AND network_id IN ($idList)";
}
} else {
$network_id = intval($filter['network_id']);
if ($network_id > 0) {
$where .= " AND network_id=$network_id";
} elseif ($network_id === 0) {
$where .= " AND (network_id IS NULL OR network_id=0)";
}
}
}
@@ -239,7 +257,51 @@ INNER JOIN `Address` ON (`Address`.`id`=`Addresstype`.`address_id`)
return $items;
}
public static function getCableRoute($cable_id) {
$route = [];
$db = FronkDB::singleton();
// SQL-Abfrage mit object_type für Dispatcher
$sql = "SELECT
fcs.station_order,
fcs.station_type,
fcs.station_id,
CASE
WHEN fcs.station_type = 'pop' THEN p.name
WHEN fcs.station_type = 'dispatcher' THEN d.description
END as station_name,
CASE
WHEN fcs.station_type = 'pop' THEN p.gps_lat
WHEN fcs.station_type = 'dispatcher' THEN d.gps_lat
END as gps_lat,
CASE
WHEN fcs.station_type = 'pop' THEN p.gps_long
WHEN fcs.station_type = 'dispatcher' THEN d.gps_long
END as gps_long,
d.object_type
FROM FiberPlanCableStation fcs
LEFT JOIN Pop p ON fcs.station_type = 'pop' AND fcs.station_id = p.id
LEFT JOIN FiberPlanDispatcher d ON fcs.station_type = 'dispatcher' AND fcs.station_id = d.id
WHERE fcs.cable_id = " . intval($cable_id) . "
ORDER BY fcs.station_order ASC";
$res = $db->query($sql);
if ($db->num_rows($res)) {
while ($data = $db->fetch_array($res)) {
$route[] = [
'order' => $data['station_order'],
'type' => $data['station_type'],
'id' => $data['station_id'],
'name' => $data['station_name'],
'gps_lat' => $data['gps_lat'],
'gps_long' => $data['gps_long'],
'object_type' => $data['object_type'] // Neu: object_type hinzugefügt
];
}
}
return $route;
}
public static function generateEndpoint($endpoint, $destination, $html = 1)
{
@@ -0,0 +1,126 @@
<?php
class FiberPlanFiber extends mfBaseModel
{
private $editor;
private $creator;
private $cable;
private $branch_cable;
private $splitter;
public function getProperty($name)
{
if ($this->$name == null) {
if (!$this->id) {
return null;
}
if ($name == "creator") {
$this->creator = mfValuecache::singleton()->get("Worker-id-" . $this->create_by);
if ($this->creator === null) {
$this->creator = new User($this->create_by);
if ($this->creator->id) {
mfValuecache::singleton()->set("Worker-id-" . $this->create_by, $this->creator);
}
}
return $this->creator;
}
if ($name == "editor") {
$this->editor = mfValuecache::singleton()->get("Worker-id-" . $this->edit_by);
if ($this->editor === null) {
$this->editor = new User($this->edit_by);
if ($this->editor->id) {
mfValuecache::singleton()->set("Worker-id-" . $this->edit_by, $this->editor);
}
}
return $this->editor;
}
if ($name == "cable" && $this->cable_id) {
$this->cable = mfValuecache::singleton()->get("FiberPlanCable-id-" . $this->cable_id);
if (!$this->cable) {
$this->cable = new FiberPlanCable($this->cable_id);
if ($this->cable->id) {
mfValuecache::singleton()->set("FiberPlanCable-id-" . $this->cable_id, $this->cable);
}
}
return $this->cable;
}
if ($name == "branch_cable" && $this->branch_cable_nr) {
$this->branch_cable = mfValuecache::singleton()->get("Cable-nr-" . $this->branch_cable_nr);
if (!$this->branch_cable) {
$cables = FiberPlanCableModel::search(['description' => $this->branch_cable_nr]);
if (count($cables) > 0) {
$this->branch_cable = $cables[0];
mfValuecache::singleton()->set("Cable-nr-" . $this->branch_cable_nr, $this->branch_cable);
}
}
return $this->branch_cable;
}
$classname = ucfirst($name);
$idfield = $name . "_id";
$this->$name = mfValuecache::singleton()->get("mfObjectmodel-$name-" . $this->$idfield);
if (!$this->$name) {
$this->$name = new $classname($this->$idfield);
}
if ($this->$name->id) {
mfValuecache::singleton()->set("mfObjectmodel-$name-" . $this->$name->id, $this->$name);
return $this->$name;
} else {
return null;
}
}
return $this->$name;
}
public function isBranchCable()
{
return ($this->branch_type == "Abzweigkabel");
}
public function isCustomerConnection()
{
return (!empty($this->home_id));
}
public function getFullPath()
{
$path = [];
$current = $this;
$path[] = [
'cable' => $current->cable,
'fiber' => $current,
'type' => 'main'
];
if ($current->isBranchCable() && $current->branch_cable) {
$branch_fiber = FiberPlanFiberModel::findByFiberNr(
$current->branch_cable->id,
$current->branch_fiber_nr,
$current->branch_bundle_nr,
$current->branch_fiber_in_bundle
);
if ($branch_fiber) {
$path[] = [
'cable' => $current->branch_cable,
'fiber' => $branch_fiber,
'type' => 'branch'
];
if ($branch_fiber->isBranchCable()) {
$sub_path = $branch_fiber->getFullPath();
$path = array_merge($path, array_slice($sub_path, 1));
}
}
}
return $path;
}
}
@@ -0,0 +1,186 @@
<?php
class FiberPlanFiberController extends mfBaseController
{
protected function init()
{
$this->needlogin = true;
$me = new User();
$me->loadMe();
$this->me = $me;
$this->layout()->set("me", $me);
if (!$me->is(["Admin"])) {
$this->redirect("Dashboard");
}
}
protected function indexAction()
{
$this->layout()->setTemplate("FiberPlanFiber/Index");
$fiberplanfibers = FiberPlanFiberModel::getAll();
$this->layout()->set("fiberplanfibers", $fiberplanfibers);
}
protected function addAction()
{
$cables=CableModel::getAll();
$this->layout()->set("cables", $cables);
$this->layout()->setTemplate("FiberPlanFiber/Form");
}
protected function editAction()
{
$id = $this->request->id;
if (!is_numeric($id) || !$id) {
$this->layout()->setFlash("Faser nicht gefunden", "error");
$this->redirect("FiberPlanFiber");
}
$fiberplanfibers = new FiberPlanFiber($id);
if ($fiberplanfibers->id != $id) {
$this->layout()->setFlash("Faser nicht gefunden", "error");
$this->redirect("FiberPlanFiber");
}
$this->layout()->set("fiberplanfibers", $fiberplanfibers);
return $this->addAction();
}
protected function getCustomerLocationAction()
{
header('Content-Type: application/json');
$address = $this->request->address;
$network_id = intval($this->request->network_id);
if (!$address) {
echo json_encode(['success' => false, 'error' => 'Keine Adresse angegeben']);
return;
}
$db = FronkDB::singleton();
$address_escaped = $db->escape($address);
$sql = "SELECT gps_lat, gps_long
FROM FiberPlanAddress
WHERE address LIKE '%$address_escaped%'
AND network_id = $network_id
AND gps_lat IS NOT NULL
AND gps_long IS NOT NULL
LIMIT 1";
$res = $db->query($sql);
if ($db->num_rows($res)) {
$data = $db->fetch_object($res);
echo json_encode([
'success' => true,
'gps_lat' => $data->gps_lat,
'gps_long' => $data->gps_long
]);
} else {
echo json_encode([
'success' => false,
'error' => 'Keine GPS-Koordinaten gefunden'
]);
}
exit;
}
protected function saveAction()
{
$r = $this->request;
$id = $r->id;
if (is_numeric($id) && $id > 0) {
$mode = "edit";
$fiberplanfibers = new FiberPlanFiber($id);
if (!$fiberplanfibers->id) {
$this->layout()->setFlash("Fasern nicht gefunden", "error");
$this->redirect("FiberPlanFiber");
}
} else {
$mode = "add";
}
$data = [];
$data['cable_id'] = trim($r->cable_id);
$data['sheet_range'] = trim($r->sheet_range);
$data['connector_nr'] = trim($r->connector_nr);
$data['fiber_nr_cable'] = trim($r->fiber_nr_cable);
$data['bundle_nr'] = trim($r->bundle_nr);
$data['fiber_nr_bundle'] = trim($r->fiber_nr_bundle);
$data['fiber_color'] = trim($r->fiber_color);
$data['branch_type'] = trim($r->branch_type);
$data['branch_cable_nr'] = trim($r->branch_cable_nr);
$data['branch_fiber_count'] = trim($r->branch_fiber_count);
$data['branch_fiber_nr'] = trim($r->branch_fiber_nr);
$data['branch_bundle_nr'] = trim($r->branch_bundle_nr);
$data['branch_fiber_in_bundle'] = trim($r->branch_fiber_in_bundle);
$data['branch_from_location'] = trim($r->branch_from_location);
$data['branch_to_location'] = trim($r->branch_to_location);
$data['branch_gis_id'] = trim($r->branch_gis_id);
$data['address'] = trim($r->address);
$data['name'] = trim($r->name);
$data['home_id'] = trim($r->home_id);
$data['location'] = trim($r->location);
$data['rack_unit'] = trim($r->rack_unit);
$data['termination_type'] = trim($r->termination_type);
$data['customer_cable_type'] = trim($r->customer_cable_type);
$data['customer_cable_fiber_nr'] = trim($r->customer_cable_fiber_nr);
$data['customer_connector_type'] = trim($r->customer_connector_type);
$data['customer_cable_spec'] = trim($r->customer_cable_spec);
$data['customer_fiber_range'] = trim($r->customer_fiber_range);
$data['splitter_location'] = trim($r->splitter_location);
$data['splitter_factor'] = trim($r->splitter_factor);
$data['uuid_qgis_kabel'] = trim($r->uuid_qgis_kabel);
$data['status'] = trim($r->status);
$data['comment'] = trim($r->comment);
foreach ($data as $key => $value) {
if ($value === '') {
$data[$key] = NULL;
}
}
if ($mode == "edit") {
$fiberplanfibers->update($data);
} else {
$fiberplanfibers = FiberPlanFiberModel::create($data);
}
$id = $fiberplanfibers->save();
if (!$id) {
$this->layout()->setFlash("Faser konnte nicht angelegt werden", "error");
$this->redirect("FiberPlanFiber");
}
if ($mode == "edit") {
$this->layout()->setFlash("Faser erfolgreich geändert", "success");
} else if ($mode = "add") {
$this->layout()->setFlash("Faser erfolgreich angelegt", "success");
}
$this->redirect("FiberPlanFiber");
}
protected function deleteAction()
{
$id = $this->request->id;
$fiberplanfibers = new FiberPlanFiber($id);
if (!$fiberplanfibers->id || $fiberplanfibers->id != $id) {
$this->layout()->setFlash("Faser nicht gefunden.", "error");
$this->redirect("FiberPlanFiber");
}
$fiberplanfibers->delete();
$this->redirect("FiberPlanFiber");
}
}
@@ -0,0 +1,314 @@
<?php
class FiberPlanFiberModel
{
private $cable_id;
private $sheet_range;
private $connector_nr;
private $fiber_nr_cable;
private $bundle_nr;
private $fiber_nr_bundle;
private $fiber_color;
private $bundle_color;
private $fiber_color_hex;
private $bundle_color_hex;
private $branch_bundle_color_hex;
private $branch_type;
private $branch_cable_nr;
private $branch_fiber_count;
private $branch_fiber_nr;
private $branch_bundle_nr;
private $branch_fiber_in_bundle;
private $branch_bundle_color;
private $branch_from_location;
private $branch_to_location;
private $branch_gis_id;
private $address;
private $name;
private $home_id;
private $location;
private $rack_unit;
private $termination_type;
private $customer_cable_type;
private $customer_cable_fiber_nr;
private $customer_connector_type;
private $customer_cable_spec;
private $customer_fiber_range;
private $splitter_location;
private $splitter_factor;
private $uuid_qgis_kabel;
private $status;
private $comment;
public static function find($data)
{
}
public static function create(array $data)
{
$model = new FiberPlanFiber();
foreach ($data as $field => $value) {
if (property_exists(get_called_class(), $field)) {
if (substr($field, 0, 5) == "vlan_" && !$value) {
$model->$field = null;
continue;
}
$model->$field = $value;
}
}
$me = mfValuecache::singleton()->get("me");
if (!$me) {
$me = new User();
$me->loadMe();
mfValuecache::singleton()->set("me", $me);
}
if ($model->create_by === null) {
$model->create_by = $me->id;
}
if ($model->edit_by === null) {
$model->edit_by = $me->id;
}
return $model;
}
public static function getOne($id)
{
if (!is_numeric($id) || !$id) {
throw new Exception("Invalid number", 400);
}
$item = [];
$db = FronkDB::singleton();
$res = $db->select("FiberPlanFiber", "*", "id=$id LIMIT 1");
if ($db->num_rows($res)) {
$data = $db->fetch_object($res);
$item = new FiberPlanFiber($data);
}
return $item;
}
public static function getAll()
{
$items = [];
$db = FronkDB::singleton();
$res = $db->select("FiberPlanFiber", "*", "1=1");
if ($db->num_rows($res)) {
while ($data = $db->fetch_object($res)) {
$items[] = new FiberPlanFiber($data);
}
}
return $items;
}
public static function getFirst()
{
$db = FronkDB::singleton();
$where = self::getSqlFilter($filter);
$res = $db->select("FiberPlanFiber", "*", "$where ");
if ($db->num_rows($res)) {
$data = $db->fetch_object($res);
$item = new FiberPlanFiber($data);
if ($item->id) {
return $item;
} else {
return null;
}
}
return null;
}
public static function search($filter)
{
$items = [];
$db = FronkDB::singleton();
$where = self::getSqlFilter($filter);
$res = $db->select("FiberPlanFiber", "*", "$where");
if ($db->num_rows($res)) {
while ($data = $db->fetch_object($res)) {
$items[] = new FiberPlanFiber($data);
}
}
return $items;
}
private static function getSqlFilter($filter)
{
$where = "1=1 ";
$db = FronkDB::singleton();
//var_dump($filter);exit;
if (array_key_exists("cable_id", $filter)) {
$cable_id = intval($filter['cable_id']);
if ($cable_id > 0) {
$where .= " AND cable_id=$cable_id";
}
}
if (array_key_exists("sheet_range", $filter)) {
$sheet_range = $db->escape($filter['sheet_range']);
if ($sheet_range) {
$where .= " AND sheet_range='$sheet_range'";
}
}
if (array_key_exists("connector_nr", $filter)) {
$connector_nr = $db->escape($filter['connector_nr']);
if ($connector_nr) {
$where .= " AND connector_nr='$connector_nr'";
}
}
if (array_key_exists("fiber_nr_cable", $filter)) {
$fiber_nr_cable = $db->escape($filter['fiber_nr_cable']);
if ($fiber_nr_cable) {
$where .= " AND fiber_nr_cable='$fiber_nr_cable'";
}
}
if (array_key_exists("home_id", $filter)) {
$home_id = $db->escape($filter['home_id']);
if ($home_id) {
$where .= " AND home_id='$home_id'";
}
}
if (array_key_exists("branch_type", $filter)) {
$branch_type = $db->escape($filter['branch_type']);
if ($branch_type) {
$where .= " AND branch_type='$branch_type'";
}
}
if (array_key_exists("status", $filter)) {
$status = $db->escape($filter['status']);
if ($status) {
$where .= " AND status='$status'";
}
}
//var_dump($filter, $where);exit;
return $where;
}
public static function findByFiberNr($cable_id, $fiber_nr_cable)
{
$db = FronkDB::singleton();
$cable_id = intval($cable_id);
$fiber_nr_cable = $db->escape($fiber_nr_cable);
$where = "cable_id=$cable_id AND fiber_nr_cable='$fiber_nr_cable'";
$res = $db->select("FiberPlanFiber", "*", "$where LIMIT 1");
if ($db->num_rows($res)) {
$data = $db->fetch_object($res);
return new FiberPlanFiber($data);
}
return null;
}
public static function getByCableAndSheet($cable_id, $sheet_range = null)
{
$items = [];
$db = FronkDB::singleton();
$where = "cable_id=$cable_id";
if ($sheet_range) {
$sheet_range = $db->escape($sheet_range);
$where .= " AND sheet_range='$sheet_range'";
}
$res = $db->select("FiberPlanFiber", "*", "$where ORDER BY connector_nr ASC");
if ($db->num_rows($res)) {
while ($data = $db->fetch_object($res)) {
$items[] = new FiberPlanFiber($data);
}
}
return $items;
}
public static function getBranchFibers($cable_id)
{
$items = [];
$db = FronkDB::singleton();
$res = $db->select("FiberPlanFiber", "*", "cable_id=$cable_id AND branch_type='Abzweigkabel'");
if ($db->num_rows($res)) {
while ($data = $db->fetch_object($res)) {
$items[] = new FiberPlanFiber($data);
}
}
return $items;
}
public static function getCustomerConnections($cable_id)
{
$items = [];
$db = FronkDB::singleton();
$res = $db->select("FiberPlanFiber", "*", "cable_id=$cable_id AND home_id IS NOT NULL AND home_id != ''");
if ($db->num_rows($res)) {
while ($data = $db->fetch_object($res)) {
$items[] = new FiberPlanFiber($data);
}
}
return $items;
}
public static function findByPosition($cable_id, $sheet_range, $connector_nr)
{
$db = FronkDB::singleton();
$cable_id = intval($cable_id);
$sheet_range = $db->escape($sheet_range);
$connector_nr = $db->escape($connector_nr);
$where = "cable_id=$cable_id AND sheet_range='$sheet_range' AND connector_nr='$connector_nr'";
$res = $db->select("FiberPlanFiber", "*", "$where LIMIT 1");
if ($db->num_rows($res)) {
$data = $db->fetch_object($res);
return new FiberPlanFiber($data);
}
return null;
}
public static function findByBranch($cable_id, $sheet_range, $branch_cable_nr, $branch_fiber_nr)
{
$db = FronkDB::singleton();
$cable_id = intval($cable_id);
$sheet_range = $db->escape($sheet_range);
$branch_cable_nr = $db->escape($branch_cable_nr);
$branch_fiber_nr = $db->escape($branch_fiber_nr);
$where = "cable_id=$cable_id AND sheet_range='$sheet_range' AND branch_cable_nr='$branch_cable_nr' AND branch_fiber_nr='$branch_fiber_nr'";
$res = $db->select("FiberPlanFiber", "*", "$where LIMIT 1");
if ($db->num_rows($res)) {
$data = $db->fetch_object($res);
return new FiberPlanFiber($data);
}
return null;
}
}
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
.printModal{font-family:sans-serif;display:flex;text-align:center;font-weight:300;font-size:30px;left:0;top:0;position:absolute;color:#0460b5;width:100%;height:100%;background-color:hsla(0,0%,100%,.91)}.printClose{position:absolute;right:10px;top:10px}.printClose:before{content:"\00D7";font-family:Helvetica Neue,sans-serif;font-weight:100;line-height:1px;padding-top:.5em;display:block;font-size:2em;text-indent:1px;overflow:hidden;height:1.25em;width:1.25em;text-align:center;cursor:pointer}
+1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+208 -390
View File
@@ -3,70 +3,68 @@ $(document).ready(function () {
Sortable.create(sortracklist, {
handle: '.move-handle',
onEnd: function () {
var popid = $('#sortracklist').data('popid');
var racksortids = [];
$('#sortracklist').find('th').each(function (index, value) {
racksortids.push($(this).data('rackid'));
});
$.post(linkSorTracklist + "&pop_id=" + popid, {
racksortids: racksortids
}, function (data) {
if (data.success === true) {
}
}, "json");
}
});
}
$('#pop-rack-div').show();
$('#rackModal').on('show.bs.modal', function (event) {
var thisclick = $(event.relatedTarget);
var rackhe = thisclick.closest('table').find('th').data('rackhe');
var rackid = thisclick.closest('table').find('th').data('rackid');
var rackname = thisclick.closest('table').find('th').data('rackname');
var minhe = 1;
var modal = $(this);
var edit = 0;
modal.find('.alert').text('');
modal.find('.alert').hide();
if (rackid === undefined) {
$('#rack-name').val('');
$('#rack-he').val('');
var popid = thisclick.data('popid');
$('#rack-add').data('popid', popid);
$('#rack-update').hide();
$('#rack-remove').hide();
$('#rack-add').show();
} else {
edit = 1;
$('#rack-remove').hide();
$('#rack-add').hide();
$('#rack-update').show();
$('#rack-he').val(rackhe);
$('#rack-name').val(rackname);
for (let i = 1; i <= rackhe; i++) {
if (!thisclick.closest('table').find('tbody').find('tr').eq(i - 1).find('td').eq(1).hasClass('he-free')) {
minhe = i;
}
}
if (minhe === 1) {
var thisclick = $(event.relatedTarget);
var rackhe = thisclick.closest('table').find('th').data('rackhe');
var rackid = thisclick.closest('table').find('th').data('rackid');
var rackname = thisclick.closest('table').find('th').data('rackname');
var minhe = 1;
var modal = $(this);
var edit = 0;
modal.find('.alert').text('');
modal.find('.alert').hide();
$('#rack-remove').data('rackid', rackid);
$('#rack-remove').show();
if (rackid === undefined) {
$('#rack-name').val('');
$('#rack-he').val('');
var popid = thisclick.data('popid');
$('#rack-add').data('popid', popid);
$('#rack-update').hide();
$('#rack-remove').hide();
$('#rack-add').show();
} else {
edit = 1;
$('#rack-remove').hide();
$('#rack-add').hide();
$('#rack-update').show();
$('#rack-he').val(rackhe);
$('#rack-name').val(rackname);
for (let i = 1; i <= rackhe; i++) {
if (!thisclick.closest('table').find('tbody').find('tr').eq(i - 1).find('td').eq(1).hasClass('he-free')) {
minhe = i;
}
$('#rack-update').data('rackid', rackid);
$('#rack-update').data('rackminhe', minhe);
}
}
)
;
$('#rackModuleModal').on('show.bs.modal', function (event) {
if (minhe === 1) {
$('#rack-remove').data('rackid', rackid);
$('#rack-remove').show();
}
$('#rack-update').data('rackid', rackid);
$('#rack-update').data('rackminhe', minhe);
}
});
$('#rackModuleModal').on('show.bs.modal', function (event) {
trigger = $(event.relatedTarget);
var destinationname = trigger.closest('table').find('th').text();
var rackhe = trigger.closest('table').find('th').data('rackhe');
@@ -74,8 +72,6 @@ $(document).ready(function () {
modal.find('.modal-title').html('<span id="module-info">Modul (' + destinationname + ')</span>');
modal.find('.alert').text('');
modal.find('.alert').hide();
$('#module-slot-div').hide();
$('#module-position-div').hide();
var options;
var selected;
var hemaxcount = 1;
@@ -84,6 +80,7 @@ $(document).ready(function () {
var side = trigger.closest('tbody').data('side');
$('#module-type option').prop('disabled', false);
var parent = trigger.closest('tr');
if (trigger.closest('tr').find('td').eq(1).html() === undefined) {
edit = 1;
parent = trigger.closest('tr').prev();
@@ -95,6 +92,7 @@ $(document).ready(function () {
}
}
}
if (parent.find('td').eq(1).data('id') || parent.find('td').eq(2).data('id') || parent.find('td').eq(3).data('id') || parent.find('td').eq(4).data('id')) {
var counttd = parent.find('td').length - 1;
var newmodule = false;
@@ -118,10 +116,8 @@ $(document).ready(function () {
$('#module-slot').html(options);
$('#module-position').html(options);
$('#module-slot-div').show();
}
else
{
} else {
$('#module-slot-div').hide();
}
$('#module-width').attr('disabled', 'disabled');
@@ -138,18 +134,20 @@ $(document).ready(function () {
$('#module-update').show();
$('#module-add').hide();
$('#module-type').val(parent.find('td').eq(1).data('type')).change();
if ($('#module-type').val() == "1") {
$('#module-device-id').hide();
$('#module-device-text').text(parent.find('td').eq(1).data('name'));
$('#module-device-text').show();
$('#module-type option').prop('disabled', true);
}
if (parent.find('td').eq(1).data('ports') != "") {
$('#module-ports').val(parent.find('td').eq(1).data('ports')).change();
$('#module-plug').val(parent.find('td').eq(1).data('plug'));
}
$('#module-name').val(parent.find('td').eq(1).data('name'));
const status = parent.find('td').eq(1).data('status');
$('#module-type').val(parent.find('td').eq(1).data('type')).change();
@@ -157,7 +155,6 @@ $(document).ready(function () {
$('#module-type option[value="1"]').prop('disabled', true);
$('#module-remove').data('moduleid', parent.find('td').eq(1).data('id'));
$('#module-update').data('moduleid', parent.find('td').eq(1).data('id'));
} else {
@@ -184,29 +181,37 @@ $(document).ready(function () {
$('#module-remove').hide();
$('#module-update').hide();
$('#module-add').show();
for (let i = 1; i <= rackhe; i++) {
if (i == trigger.data('he')) {
selected = 'selected="selected"';
} else {
selected = '';
}
if (trigger.closest('tbody').find('tr').eq(i - 1).find('td').eq(1).hasClass('he-free')) {
options = options + '<option ' + selected + ' value="' + i + '">' + i + '</option>';
}
if (hemaxcountactive == 1 && i > trigger.data('he') && !trigger.closest('tbody').find('tr').eq(i - 1).find('td').eq(1).hasClass('he-free')) {
hemaxcountactive = 0;
}
if (hemaxcountactive == 1 && i > trigger.data('he') && trigger.closest('tbody').find('tr').eq(i - 1).find('td').eq(1).hasClass('he-free')) {
hemaxcount++;
}
}
$('#he-start-div').html(`<select required="required" id="module-he-start" name="module-he-start" class="form-control">` + options + `</select>`);
options = "";
selected = "";
for (let i = 1; i <= hemaxcount; i++) {
options = options + '<option ' + selected + ' value="' + i + '">' + i + '</option>';
}
$('#he-count-div').html(`<select required="required" id="module-he-count" name="module-he-count" class="form-control">` + options + `</select>`);
if (edit == 0) {
$('#module-name-div').show();
$('#module-name').removeAttr('disabled');
@@ -225,9 +230,9 @@ $(document).ready(function () {
$('#module-position-div').hide();
$('#module-device-text').hide();
$('#module-device-id').show();
}
}
$('#module-side').val(side);
});
@@ -236,7 +241,6 @@ $(document).ready(function () {
$('#module-name-div').hide();
$('#module-name').attr('disabled', 'disabled');
$('#module-device-div').show();
$('#module-device-id').removeAttr('disabled');
$('#module-ports-div').hide();
$('#module-plug-div').hide();
@@ -266,6 +270,7 @@ $(document).ready(function () {
var hemaxcountactive = 1
var options;
var selected;
for (let i = 1; i <= rackhe; i++) {
if (hemaxcountactive == 1 && i > $(this).val() && !trigger.closest('tbody').find('tr').eq(i - 1).find('td').eq(1).hasClass('he-free')) {
hemaxcountactive = 0;
@@ -275,9 +280,11 @@ $(document).ready(function () {
hemaxcount++;
}
}
for (let i = 1; i <= hemaxcount; i++) {
options = options + '<option ' + selected + ' value="' + i + '">' + i + '</option>';
}
$('#he-count-div').html(`<select required="required" id="module-he-count" name="module-he-count" class="form-control">` + options + `</select>`);
});
@@ -286,15 +293,14 @@ $(document).ready(function () {
var rackid = trigger.closest('table').find('th').data('rackid');
var endhe = parseInt($.trim($('#module-he-start').val())) + parseInt($.trim($('#module-he-count').val())) - 1;
if (!$.trim($('#module-name').val()) && $.trim($('#module-type').val()) != "1" && $.trim($('#module-type').val()) != "0") {
error = "Modul Name darf nicht leer sein";
}
if ($.trim($('#module-type').val()) == "1" && !$.trim($('#module-device-id').val())) {
error = "Kein Device ausgewählt";
}
if (!error) {
$.post(linkAddModule + "&poprack_id=" + rackid, {
side: $.trim($('#module-side').val()),
@@ -308,14 +314,11 @@ $(document).ready(function () {
width: $.trim($('#module-width').val()),
status: $.trim($('#module-status').val()),
position: $.trim($('#module-position').val())
}, function (data) {
if (data.success === true) {
$('#rackModuleModal').modal('toggle');
var currentSide = trigger.closest('tbody').data('side');
$.get(linkGenerateRack + "&id=" + rackid + "&side=" + currentSide, function (data, status) {
trigger.closest('tbody').html(data);
});
updateAllRackViews(rackid, currentSide);
}
}, "json");
} else {
@@ -323,9 +326,11 @@ $(document).ready(function () {
$(this).closest('.modal').find('.alert').show();
}
});
$("body").on("click", "#module-remove", function () {
var moduleid = $(this).data('moduleid');
var rackid = trigger.closest('table').find('th').data('rackid');
if (confirm("Modul entfernen?")) {
let side = trigger.closest('tbody').data('side');
$.post(linkRemoveModule, {
@@ -333,21 +338,22 @@ $(document).ready(function () {
}, function (data) {
if (data.success === true) {
$('#rackModuleModal').modal('toggle');
$.get(linkGenerateRack + "&id=" + rackid + "&side=" + side, function (data, status) {
trigger.closest('tbody').html(data);
});
updateAllRackViews(rackid, side);
}
}, "json");
}
});
$("body").on("click", "#module-update", function () {
var moduleid = $(this).data('moduleid');
var rackid = trigger.closest('table').find('th').data('rackid');
var error;
let side = trigger.closest('tbody').data('side');
if (!$.trim($('#module-name').val()) && $.trim($('#module-type').val()) != "1" && $.trim($('#module-type').val()) != "0") {
error = "Modul Name darf nicht leer sein";
}
if (!error) {
$.post(linkUpdateModule, {
id: moduleid,
@@ -359,9 +365,7 @@ $(document).ready(function () {
}, function (data) {
if (data.success === true) {
$('#rackModuleModal').modal('toggle');
$.get(linkGenerateRack + "&id=" + rackid + "&side=" + side, function (data, status) {
trigger.closest('tbody').html(data);
});
updateAllRackViews(rackid, side);
}
}, "json");
} else {
@@ -378,6 +382,7 @@ $(document).ready(function () {
if ($('#rack-he').val() < rackmin) {
error = "Minimale Höheneinheiten: " + rackmin;
}
if ($('#rack-he').val() > 60) {
error = "Maximale Höheneinheiten: 60";
}
@@ -389,14 +394,15 @@ $(document).ready(function () {
if (!$.trim($('#rack-he').val())) {
error = "Höheneinheiten darf nicht leer sein";
}
if (!$.trim($('#rack-name').val())) {
error = "Schrank Name darf nicht leer sein";
}
if (!error) {
$.post(linkEditRack + "&poprack_id=" + rackid, {
name: $.trim($('#rack-name').val()),
he: $.trim($('#rack-he').val())
}, function (data) {
if (data.success === true) {
$('#rackModal').modal('toggle');
@@ -408,6 +414,7 @@ $(document).ready(function () {
$(this).closest('.modal').find('.alert').show();
}
});
$("body").on("click", "#rack-add", function () {
var popid = $(this).data('popid');
var error;
@@ -415,6 +422,7 @@ $(document).ready(function () {
if ($('#rack-he').val() < 1) {
error = "Minimale Höheneinheiten: " + 1;
}
if ($('#rack-he').val() > 60) {
error = error = "Maximale Höheneinheiten: 60";
}
@@ -426,14 +434,15 @@ $(document).ready(function () {
if (!$.trim($('#rack-he').val())) {
error = "Höheneinheiten darf nicht leer sein";
}
if (!$.trim($('#rack-name').val())) {
error = "Schrank Name darf nicht leer sein";
}
if (!error) {
$.post(linkAddRack + "&pop_id=" + popid, {
name: $.trim($('#rack-name').val()),
he: $.trim($('#rack-he').val())
}, function (data) {
if (data.success === true) {
$('#rackModal').modal('toggle');
@@ -457,33 +466,29 @@ $(document).ready(function () {
location.reload();
}
}, "json");
});
$("body").on("change", "#module-width", function () {
if ($(this).val() == "12") {
$('#module-position-div').hide();
} else if ($(this).val() == "6") {
$('#module-position').html(`<option value="1">1</option>
<option value="2">2</option>
`);
<option value="2">2</option>`);
$('#module-position-div').show();
} else if ($(this).val() == "4") {
$('#module-position').html(`<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
`);
<option value="3">3</option>`);
$('#module-position-div').show();
} else if ($(this).val() == "3") {
$('#module-position').html(`<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
`);
<option value="4">4</option>`);
$('#module-position-div').show();
}
});
$("body").on("change", "#module-ports", function () {
var plugs = [];
if ($(this).find(':selected').data('plugs') !== undefined) {
@@ -504,6 +509,7 @@ $(document).ready(function () {
$("body").on("change", "#module-slot", function () {
$('#module-position').val($(this).val());
var parent = trigger.closest('tr');
if (trigger.closest('tr').find('td').eq(1).html() === undefined) {
parent = trigger.closest('tr').prev();
for (let i = 1; i <= rackhe; i++) {
@@ -514,31 +520,35 @@ $(document).ready(function () {
}
}
}
var newmodule = false;
var tdnumber = parseInt($(this).val())
if (parent.find('td').eq(tdnumber).data('id') === undefined) {
newmodule = true;
}
if (!newmodule) {
$('#module-remove').show();
$('#module-update').show();
$('#module-add').hide();
$('#module-type').val(parent.find('td').eq(tdnumber).data('type')).change();
if ($('#module-type').val() == "1") {
$('#module-device-id').hide();
$('#module-device-text').text(parent.find('td').eq(tdnumber).data('name'));
$('#module-device-text').show();
}
if (parent.find('td').eq(tdnumber).data('ports') != "") {
$('#module-ports').val(parent.find('td').eq(tdnumber).data('ports')).change();
$('#module-plug').val(parent.find('td').eq(tdnumber).data('plug'));
}
$('#module-type').attr('disabled', 'disabled');
$('#module-name').val(parent.find('td').eq(tdnumber).data('name'));
$('#module-remove').data('moduleid', parent.find('td').eq(tdnumber).data('id'));
$('#module-update').data('moduleid', parent.find('td').eq(tdnumber).data('id'));
} else {
@@ -562,6 +572,7 @@ $(document).ready(function () {
$('#module-device-text').hide();
}
});
$("body").on("click", ".switch-rack-side", function () {
var icon = $(this);
var rackTh = icon.closest('th');
@@ -572,10 +583,13 @@ $(document).ready(function () {
var currentSide = rackBody.data('side');
var newSide = (currentSide === 'front') ? 'back' : 'front';
if (icon.hasClass('is-loading')) {
return;
}
icon.addClass('is-loading fa-spin');
rackTable.fadeOut(150, function () {
$.get(linkGenerateRack + "&id=" + rackId + "&side=" + newSide, function (data, status) {
rackBody.html(data);
@@ -586,6 +600,7 @@ $(document).ready(function () {
} else {
sideIndicator.text('- Rückseite');
}
rackTable.fadeIn(150);
}).fail(function () {
rackBody.html('<tr><td colspan="13" class="text-center text-danger p-3">Fehler beim Laden.</td></tr>');
@@ -595,335 +610,138 @@ $(document).ready(function () {
});
});
});
let currentCableTrigger;
$("body").on("click", ".cable-container", function (e) {
$("body").on("click", ".rack-fullscreen-btn", function (e) {
e.stopPropagation();
let parentTd = $(this).closest('td');
currentCableTrigger = parentTd;
let moduleId = parentTd.data('id');
let totalPorts = parentTd.data('ports');
let moduleName = parentTd.data('name');
var $originalTable = $(this).closest('table');
var rackName = $(this).closest('th').find('.rack-name').text().trim();
var $clonedTable = $originalTable.clone();
$('#cableModal #cable-poprackmodule-id').val(moduleId);
$('#cableModal #port-start, #cableModal #port-end').attr('max', totalPorts);
$('#cableModal #cable-name, #cableModal #port-start, #cableModal #port-end').val('');
$('#cableModal #cable-modal-alert').hide();
$('#cableModal .modal-title').text('Kabel auf Panel ' + moduleName);
$clonedTable.find('.move-handle, .switch-rack-side, .fa-edit, .fa-expand').remove();
$('#cableModal').modal('show');
});
$("body").on("click", "#cable-add-button", function () {
if (!currentCableTrigger) return;
let rackid = currentCableTrigger.closest('table').find('th').data('rackid');
let modal = $('#cableModal');
let alert = modal.find('#cable-modal-alert');
let data = {
poprackmodule_id: modal.find('#cable-poprackmodule-id').val(),
cable_name: modal.find('#cable-name').val().trim(),
port_start: parseInt(modal.find('#port-start').val()),
port_end: parseInt(modal.find('#port-end').val()),
fiber_start: parseInt(modal.find('#fiber-start').val()) || null,
fiber_end: parseInt(modal.find('#fiber-end').val()) || null,
description: modal.find('#cable-description').val().trim()
};
if (!data.cable_name || !data.port_start || !data.port_end) {
alert.text('Bitte alle Felder ausfüllen.').show();
return;
}
if (data.port_start > data.port_end) {
alert.text('Start-Port muss kleiner oder gleich dem End-Port sein.').show();
return;
}
if (data.fiber_start && data.fiber_end) {
if (data.fiber_start > data.fiber_end) {
alert.text('Faser-Start muss kleiner oder gleich Faser-Ende sein.').show();
return;
}
const portCount = data.port_end - data.port_start + 1;
const fiberCount = data.fiber_end - data.fiber_start + 1;
if (fiberCount > portCount) {
alert.text('Die Anzahl der Fasern (' + fiberCount + ') darf nicht größer als die Anzahl der Ports (' + portCount + ') sein.').show();
return;
}
}
$.post(linkAddCable, data, function (response) {
if (response.success) {
modal.modal('hide');
let currentSide = currentCableTrigger.closest('tbody').data('side');
$.get(linkGenerateRack + "&id=" + rackid + "&side=" + currentSide, function (html) {
currentCableTrigger.closest('tbody').html(html);
});
} else {
alert.text(response.error || 'Fehler beim Speichern.').show();
}
}, 'json');
});
let currentCableElement;
let cableMenuPopper = null;
function showCableMenuAtEvent(e) {
const $menu = $('#cable-context-menu');
if (!$menu.length) return;
$menu.removeAttr('x-placement');
if ($menu.parent()[0] !== document.body) $menu.detach().appendTo('body');
$menu.css({display: 'block'}).addClass('show');
const el = $menu[0];
const w = $menu.outerWidth(), h = $menu.outerHeight();
const vw = window.innerWidth, vh = window.innerHeight;
let x = e.clientX, y = e.clientY;
if (x + w > vw) x = vw - w - 6;
if (y + h > vh) y = vh - h - 6;
el.style.setProperty('position', 'fixed', 'important');
el.style.setProperty('left', x + 'px', 'important');
el.style.setProperty('top', y + 'px', 'important');
el.style.setProperty('z-index', '2147483000', 'important');
}
$('body').on('contextmenu click', '.cable-item', function (e) {
if (e.type === 'contextmenu' || e.type === 'click') {
if (e.which !== 1) return;
e.preventDefault();
e.stopPropagation();
currentCableElement = $(this);
$('.dropdown-menu').not('#cable-context-menu').removeClass('show').hide();
showCableMenuAtEvent(e);
requestAnimationFrame(() => {
const el = document.getElementById('cable-context-menu');
console.log('OPEN fired, ctx styles =', el ? el.style.cssText : 'NO ELEMENT');
});
}
});
function hideCableMenu() {
const $m = $('#cable-context-menu');
const el = $m[0];
if (!$m.length) return;
$m.removeClass('show').hide();
if (el) {
el.style.removeProperty('top');
el.style.removeProperty('left');
el.style.removeProperty('position');
el.style.removeProperty('z-index');
}
}
$('#cable-context-menu')
.off('mousedown.ctx click.ctx contextmenu.ctx')
.on('mousedown.ctx click.ctx contextmenu.ctx', function (e) {
e.stopPropagation();
$clonedTable.css({
'width': '800px',
'margin': '0 auto',
'background-color': '#fff',
'box-shadow': 'none'
});
$(document)
.off('mousedown.ctx click.ctx contextmenu.ctx keydown.ctx')
.on('mousedown.ctx click.ctx contextmenu.ctx', function (e) {
if ($(e.target).closest('#cable-context-menu').length) return;
hideCableMenu();
})
.on('keydown.ctx', function (e) {
if (e.key === 'Escape' || e.keyCode === 27) hideCableMenu();
});
$(window)
.off('scroll.ctx resize.ctx')
.on('scroll.ctx resize.ctx', hideCableMenu);
$('#cable-context-menu .dropdown-item').on('click', function (e) {
e.preventDefault();
e.stopPropagation();
hideCableMenu();
const action = $(this).data('action');
const rackid = currentCableElement.closest('table').find('th').data('rackid');
const cableId = currentCableElement.data('cable-id');
const cableName = currentCableElement.data('cable-name');
switch (action) {
case 'view':
alert("Detailansicht für Kabel '" + cableName + "' (ID: " + cableId + ")");
break;
case 'edit': {
const cableId = currentCableElement.data('cable-id');
const cableName = currentCableElement.data('cable-name');
const portStart = currentCableElement.data('port-start');
const portEnd = currentCableElement.data('port-end');
const fiberStart = currentCableElement.data('fiber-start');
const fiberEnd = currentCableElement.data('fiber-end');
const description = currentCableElement.data('description');
$('#edit-cable-id').val(cableId);
$('#edit-cable-name').val(cableName);
$('#edit-port-start').val(portStart);
$('#edit-port-end').val(portEnd);
$('#edit-fiber-start').val(fiberStart);
$('#edit-fiber-end').val(fiberEnd);
$('#edit-cable-description').val(description);
$('#cable-edit-modal-alert').hide();
$('#cableEditModal').modal('show');
break;
}
case 'delete':
if (confirm("Kabel '" + cableName + "' wirklich entfernen?")) {
$.post(linkRemoveCable, {id: cableId}, function (response) {
if (response.success) {
let currentSide = currentCableElement.closest('tbody').data('side');
$.get(linkGenerateRack + "&id=" + rackid + "&side=" + currentSide, function (html) {
currentCableElement.closest('tbody').html(html);
});
} else {
alert('Fehler beim Löschen des Kabels.');
}
}, 'json');
}
break;
}
$('#overlayTitle').text(rackName);
$('#overlayContent').html($clonedTable);
$('#customRackOverlay').fadeIn();
$('body').css('overflow', 'hidden');
$('#overlayContent [data-toggle="popover"]').popover();
});
$("body").on("click", "#cable-update-button", function () {
const modal = $('#cableEditModal');
const cableId = modal.find('#edit-cable-id').val();
const rackid = currentCableElement.closest('table').find('th').data('rackid');
const data = {
id: cableId,
cable_name: modal.find('#edit-cable-name').val().trim(),
port_start: parseInt(modal.find('#edit-port-start').val()),
port_end: parseInt(modal.find('#edit-port-end').val()),
fiber_start: parseInt(modal.find('#edit-fiber-start').val()) || null,
fiber_end: parseInt(modal.find('#edit-fiber-end').val()) || null,
description: modal.find('#edit-cable-description').val().trim()
};
if (data.fiber_start && data.fiber_end) {
if (data.fiber_start > data.fiber_end) {
modal.find('#cable-edit-modal-alert').text('Faser-Start muss kleiner oder gleich Faser-Ende sein.').show();
return;
}
const portCount = data.port_end - data.port_start + 1;
const fiberCount = data.fiber_end - data.fiber_start + 1;
if (fiberCount > portCount) {
modal.find('#cable-edit-modal-alert').text('Die Anzahl der Fasern (' + fiberCount + ') darf nicht größer als die Anzahl der Ports (' + portCount + ') sein.').show();
return;
}
}
$.post(linkUpdateCable, data, function (response) {
if (response.success) {
modal.modal('hide');
let currentSide = currentCableElement.closest('tbody').data('side');
$.get(linkGenerateRack + "&id=" + rackid + "&side=" + currentSide, function (html) {
currentCableElement.closest('tbody').html(html);
});
} else {
modal.find('#cable-edit-modal-alert').text(response.error || 'Fehler beim Speichern.').show();
}
}, 'json');
});
function updateAllRackViews(rackId, side) {
$.get(linkGenerateRack + "&id=" + rackId + "&side=" + side, function (data) {
var $targets = $('tbody[id="rack-body-' + rackId + '"]');
$targets.html(data);
$targets.data('side', side);
$targets.find('[data-toggle="popover"]').popover();
});
function initCableSearch() {
// Zerstöre eine eventuell bereits existierende Select2-Instanz
if ($('#cable-search').data('select2')) {
$('#cable-search').select2('destroy');
}
$('#cable-search').empty().append(new Option());
// 1. Kabel nach Namen gruppieren und Fasern summieren
let cableGroups = {};
$('.cable-item').each(function () {
const $cableElement = $(this);
const cableName = $cableElement.data('cable-name');
// Überspringe Elemente ohne Kabelnamen
if (!cableName) return;
// Berechne die Anzahl der Fasern für DIESES Segment
let fiberCount = 0;
const fiberStart = parseInt($cableElement.data('fiber-start'));
const fiberEnd = parseInt($cableElement.data('fiber-end'));
if (!isNaN(fiberStart) && !isNaN(fiberEnd)) {
fiberCount = (fiberEnd - fiberStart) + 1;
}
function printRackOverlay(orientation = 'landscape') {
printJS({
printable: 'overlayContent',
type: 'html',
targetStyles: ['*'],
style: `
@page {
size: ${orientation};
margin: 5mm;
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 5px;
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}
// Wenn die Gruppe für diesen Namen noch nicht existiert, erstelle sie
if (!cableGroups[cableName]) {
cableGroups[cableName] = {
totalFibers: 0
};
* {
color: #000 !important;
text-shadow: none !important;
}
// Addiere die Fasern zur Gesamtsumme der Gruppe
cableGroups[cableName].totalFibers += fiberCount;
});
// 2. Erstelle die Datenquelle für Select2 aus den gruppierten Daten
let cableSearchData = [];
for (const name in cableGroups) {
let label = `Kabel: ${name}`;
if (cableGroups[name].totalFibers > 0) {
label += ` (Gesamt: ${cableGroups[name].totalFibers} Fasern)`;
table {
width: 100% !important;
border-collapse: collapse !important;
table-layout: fixed !important;
font-size: ${orientation === 'landscape' ? '10px' : '8px'} !important;
}
td, th {
padding: 1px !important;
border: 1px solid #666 !important;
overflow: hidden;
}
// Die "id" ist jetzt der eindeutige Kabelname
cableSearchData.push({
id: name,
text: label
});
}
.rack-color-lwl { background-color: #81df44 !important; }
.rack-color-lwl-planned { background-color: #ff0000 !important; }
.rack-color-device { background-color: #b6c6f0 !important; }
.rack-color-infra { background-color: #f7b876 !important; }
.rack-color-panel { background-color: #96f3de !important; }
.rack-color-blocked { background-color: #ff0000 !important; }
.rack-color-rpanel { background-color: #58c9f0 !important; }
.cable-container {
display: flex !important;
flex-direction: row !important;
width: 100% !important;
height: 100% !important;
min-height: 40px;
align-items: stretch !important;
background-color: #fffbe9 !important;
}
// 3. Select2 mit den gruppierten Daten initialisieren
$('#cable-search').select2({
data: cableSearchData,
placeholder: "Kabelname suchen...",
allowClear: true,
});
}
.cable-item {
display: flex !important;
flex-direction: column !important;
justify-content: flex-start !important;
align-items: flex-start !important;
box-sizing: border-box !important;
overflow: hidden !important;
padding: 2px !important;
text-align: left !important;
border-right: 1px solid #797979 !important;
background-color: #e1ffdf !important;
}
$('#cable-search').on('select2:select', function (e) {
const selectedData = e.params.data;
.hide-if-narrow { display: block !important; }
.cable-item b {
font-size: ${orientation === 'landscape' ? '9px' : '7px'} !important;
white-space: normal !important;
line-height: 1.1 !important;
width: 100%;
text-align: center;
display: block;
margin-bottom: 2px;
}
// Die "id" aus der Auswahl ist jetzt der Kabelname
const selectedCableName = selectedData.id;
if (!selectedCableName) return;
.cable-description {
white-space: normal !important;
font-size: ${orientation === 'landscape' ? '8px' : '7px'} !important;
line-height: 1.0 !important;
font-style: italic !important;
width: 100%;
}
.port-ranges-container {
display: flex !important;
flex-direction: column !important;
font-size: ${orientation === 'landscape' ? '8px' : '7px'} !important;
margin-top: 2px !important;
line-height: 1.0 !important;
width: 100%;
}
// 1. Finde ALLE .cable-item Elemente, die diesen Kabelnamen haben
const $targetElements = $('.cable-item[data-cable-name="' + selectedCableName + '"]');
if ($targetElements.length === 0) {
console.error("Keine Kabel-Elemente mit dem Namen '" + selectedCableName + "' gefunden.");
return;
}
// Alle alten Markierungen entfernen
$('.cable-highlight, .cable-flash').removeClass('cable-highlight cable-flash');
$('.module-highlight').removeClass('module-highlight');
// 2. Scrolle zum ERSTEN gefundenen Element
// 3. Hebe ALLE gefundenen Elemente und ihre zugehörigen Module hervor
$targetElements.each(function () {
const $el = $(this);
const $parentModule = $el.closest('td');
$el.addClass('cable-highlight cable-flash');
$parentModule.addClass('module-highlight');
});
#overlayContent { width: 100%; display: block; }
.rack-fullscreen-btn, .switch-rack-side, .move-handle, .fa-edit {
display: none !important;
}
`
});
$('#cable-search').on('change', function (e) {
if ($(this).val() === null || $(this).val() === '') {
$('.cable-highlight, .cable-flash').removeClass('cable-highlight cable-flash');
$('.module-highlight').removeClass('module-highlight');
}
});
initCableSearch();
});
}