Features:

* Komplettes kabelmanagement auf Rack He Modul Basis
This commit is contained in:
Daniel Spitzer
2025-10-12 12:19:54 +02:00
parent e17e638263
commit 4ef8e733a3
10 changed files with 1505 additions and 306 deletions

View File

@@ -24,8 +24,8 @@
.rack-color-lwl { .rack-color-lwl {
background: rgb(159, 255, 96); background: rgb(159, 255, 96);
background: linear-gradient(180deg, rgb(129 237 58) 0%, rgb(92 211 15) 100%); background: linear-gradient(180deg, rgb(129 223 68) 0%, rgb(62 155 3) 100%);
font-weight: 500; color: #000000;
padding-top: 2px; padding-top: 2px;
} }
@@ -97,12 +97,177 @@
background-color: #d7d7d7; background-color: #d7d7d7;
opacity: 1; opacity: 1;
} }
.switch-rack-side { .switch-rack-side {
margin-right: 8px; margin-right: 8px;
margin-top: 2px; margin-top: 2px;
cursor: pointer; 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> </style>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
@@ -130,9 +295,9 @@
</div> </div>
</div> </div>
<?php <?php
$plugs[1] = "LC"; $plugs[1] = "LC/APC";
$plugs[2] = "SC"; $plugs[2] = "SC/APC";
$plugs[3] = "E2000"; $plugs[3] = "E2000/APC";
$vlans = ""; $vlans = "";
@@ -183,11 +348,15 @@ if (!empty(trim($pops->vlan_ipv6)))
</tr> </tr>
<tr> <tr>
<th class="text-nowrap">Dokustand | Status</th> <th class="text-nowrap">Dokustand | Status</th>
<td><?= ($pops->doku_date) ? date("d.m.Y",$pops->doku_date) : '-' ?><span class="ml-2 mr-2">|</span><?= ($pops->state) ? $stateArray[$pops->state] : '-' ?> </td> <td><?= ($pops->doku_date) ? date("d.m.Y", $pops->doku_date) : '-' ?><span
class="ml-2 mr-2">|</span><?= ($pops->state) ? $stateArray[$pops->state] : '-' ?>
</td>
</tr> </tr>
<tr> <tr>
<th class="text-nowrap">Fotospeicherort</th> <th class="text-nowrap">Fotospeicherort</th>
<td><?php if ($pops->folder_link): ?> <a href="file://<?= $pops->folder_link ?>"><?= $pops->folder_link ?></a><?php endif; ?></td> <td><?php if ($pops->folder_link): ?> <a
href="file://<?= $pops->folder_link ?>"><?= $pops->folder_link ?></a><?php endif; ?>
</td>
</tr> </tr>
<tr> <tr>
<th class="text-nowrap">Interne Notiz</th> <th class="text-nowrap">Interne Notiz</th>
@@ -341,6 +510,8 @@ if (!empty(trim($pops->vlan_ipv6)))
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="card">
<div class="card-body">
<div class="form-group row " id="module-slot-div" style="display:none"> <div class="form-group row " id="module-slot-div" style="display:none">
<div class="col-lg-1"></div> <div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="module-slot">19 Zoll Position</label> <label class="col-lg-4 col-form-label" for="module-slot">19 Zoll Position</label>
@@ -373,6 +544,17 @@ if (!empty(trim($pops->vlan_ipv6)))
</div> </div>
</div> </div>
<div class="form-group row" id="module-status-div" style="display: none;">
<div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="module-status">Status*</label>
<div class="col-lg-6">
<select required="required" id="module-status" name="module-status"
class="form-control">
<option value="productive">Produktiv</option>
<option value="planned">Geplant</option>
</select>
</div>
</div>
<div class="form-group row" id="module-name-div"> <div class="form-group row" id="module-name-div">
<div class="col-lg-1"></div> <div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="module-name">Modul Name*</label> <label class="col-lg-4 col-form-label" for="module-name">Modul Name*</label>
@@ -459,6 +641,8 @@ if (!empty(trim($pops->vlan_ipv6)))
</div> </div>
<div class="alert alert-danger text-center" role="alert" style="display: none"></div> <div class="alert alert-danger text-center" role="alert" style="display: none"></div>
</div> </div>
</div>
</div>
<div class="modal-footer"> <div class="modal-footer">
<button id="module-add" type="button" class="btn btn-primary">Modul hinzufügen</button> <button id="module-add" type="button" class="btn btn-primary">Modul hinzufügen</button>
<button id="module-update" type="button" class="btn btn-primary">Modul Speichern</button> <button id="module-update" type="button" class="btn btn-primary">Modul Speichern</button>
@@ -471,17 +655,30 @@ if (!empty(trim($pops->vlan_ipv6)))
</div> </div>
</div> </div>
<div class="row" id="pop-rack-div" style="display:none"> <div class="row" id="pop-rack-div" style="display:none">
<div class="col-12"> <div class="col-12">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
<div class="col-12 card-border"> <div class="col-12 card-border">
<div> <div class="row align-items-center mb-2">
<h4>Schränke </h4> <div class="col-md-2">
<h4>Schränke</h4>
</div> </div>
<div class="col-md-3 justify-content-center">
<div class="">
<label for="cable-search" class="mr-2">Kabelsuche:</label>
<select id="cable-search" class="form-control form-select">
<option></option>
</select>
</div>
</div>
</div>
<div id="sortracklist" class="row" data-popid="<?= $pops->id ?>"> <div id="sortracklist" class="row" data-popid="<?= $pops->id ?>">
<?php foreach ($popracks as $poprack) : ?> <?php foreach ($popracks as $poprack) : ?>
<div class="col-lg-3 col-12 list-group-item-move mb-3"> <div class="col-lg-4 col-12 list-group-item-move mb-3">
<table class="w-100 p-0 font-13" <table class="w-100 p-0 font-13"
style="max-width: 100%; box-shadow: 2px 2px 5px 2px rgba(0,0,0,0.31);border-radius: 5px 5px 5px 5px;"> style="max-width: 100%; box-shadow: 2px 2px 5px 2px rgba(0,0,0,0.31);border-radius: 5px 5px 5px 5px;">
<thead> <thead>
@@ -491,8 +688,10 @@ if (!empty(trim($pops->vlan_ipv6)))
data-rackhe="<?= $poprack['rack']['he'] ?>" data-rackhe="<?= $poprack['rack']['he'] ?>"
data-rackid="<?= $poprack['rack']['id']; ?>"><span data-rackid="<?= $poprack['rack']['id']; ?>"><span
class="rack-name"><i 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> class="fa-regular fa-arrows-up-down-left-right move-handle float-left"></i><?= $poprack['rack']['name']; ?>&nbsp;<span
<i class="fas fa-sync-alt float-right switch-rack-side" title="Seite wechseln"></i> class="rack-side-indicator font-weight-normal">-&nbsp;Vorderseite</span></span>
<i class="fas fa-sync-alt float-right switch-rack-side"
title="Seite wechseln"></i>
<i class="far fa-edit float-right" title="Bearbeiten" <i class="far fa-edit float-right" title="Bearbeiten"
data-toggle="modal" data-target="#rackModal"></i> data-toggle="modal" data-target="#rackModal"></i>
@@ -501,117 +700,10 @@ if (!empty(trim($pops->vlan_ipv6)))
</thead> </thead>
<tbody id="rack-body-<?= $poprack['rack']['id'] ?>" data-side="front"> <tbody id="rack-body-<?= $poprack['rack']['id'] ?>" data-side="front">
<?php <?php
$cellwidth = 227;
$blocktd = 0;
for ($i = 1; $i <= $poprack['rack']['he']; $i++) : ?>
<tr>
<td class="border-right w-15 p-0 pl-1 pr-1 border-bottom font-13 rack-he user-select-none"
data-toggle="modal" data-target="#rackModuleModal"
style="cursor: pointer" data-he="<?= $i; ?>">He<?= $i; ?></td>
<?php
$modules_to_render = $poprack['modules']['front'] ?? []; $modules_to_render = $poprack['modules']['front'] ?? [];
foreach ($modules_to_render as $module) { $rack_he = $poprack['rack']['he'];
include('_rack_body.php');
if ($module['start_he'] == $i) {
$modulestart = 1;
$modulelenght = $module['end_he'] - $module['start_he'] + 1;
$position = 1;
$slotcounter = 0;
$calcwidth = 0;
$extText = "";
$extTextspan = "";
foreach ($module['slots'] as $slots) {
var_dump();
$extText = "";
$title = $slots['modulname'];
if ($slots['type'] == '0') {
$color = '#9fff6e';
$colorclass = "rack-color-lwl";
if ($slots['plug']) {
$extText = " (" . $plugs[$slots['plug']] . "/" . $slots['ports'] . "P)";
$extTextspan = "<span style='font-size: 12px;font-weight: 400'>" . $extText . "</span>";
}
} else if ($slots['type'] == '1') {
$slots['modulname'] = '<a style="color: #000000;text-decoration: underline;" target="_self" href="' . self::getUrl("Device", "detail", ["id" => $slots['device_id']]) . '">' . $slots['modulname'] . '</a>';
?> ?>
<script>
$("#module-device-id option[value='<?= $slots['device_id'] ?>']").remove();
</script>
<?php
$color = '#b6c6ff';
$colorclass = "rack-color-device";
} else if ($slots['type'] == '2') {
$color = '#ff8100';
$colorclass = "rack-color-rpanel";
} else if ($slots['type'] == '3') {
$color = '#ffad59';
$colorclass = "rack-color-infra";
} else if ($slots['type'] == '4') {
$color = '#96f3de';
$colorclass = "rack-color-panel";
} else if ($slots['type'] == '5') {
$color = '#96f3de';
$colorclass = "rack-color-blocked";
}
if ($slots['width'] == "12") {
$width = "85%";
$maxwidth = $cellwidth;
} else if ($slots['width'] == "6") {
$width = "42%";
$maxwidth = intval($cellwidth / 2);
} else if ($slots['width'] == "4") {
$width = "28%";
$maxwidth = intval($cellwidth / 3);
} else if ($slots['width'] == "3") {
$width = "21%";
$maxwidth = intval($cellwidth / 4);
}
if ($slots['width'] < 12 && $position != $slots['position']) {
echo '<td colspan="' . $slots['width'] . '" style="width: ' . $width . ';" class="text-center border-top border-bottom border-right pl-1 pr-1" rowspan="' . $modulelenght . '">frei</td>';
$position++;
$calcwidth = $calcwidth + $slots['width'];
}
if ($slots['width'] < 12 && $position != $slots['position']) {
echo '<td colspan="' . $slots['width'] . '" style="width: ' . $width . ';" class="text-center border-top border-bottom border-right pl-1 pr-1" rowspan="' . $modulelenght . '">frei</td>';
$position++;
$calcwidth = $calcwidth + $slots['width'];
}
if ($slots['width'] < 12 && $position != $slots['position']) {
echo '<td colspan="' . $slots['width'] . '" style="width: ' . $width . ';" class="text-center border-top border-bottom border-right pl-1 pr-1" rowspan="' . $modulelenght . '">frei</td>';
$position++;
$calcwidth = $calcwidth + $slots['width'];
}
echo '<td title="' . $title . $extText . '" colspan="' . $slots['width'] . '" class="text-center border-top border-bottom border-right pl-1 pr-1 ' . $colorclass . '" data-id="' . $slots['moduleid'] . '" data-type="' . $slots['type'] . '" data-name="' . $title . '" data-starthe="' . $module['start_he'] . '" data-ports="' . $slots['ports'] . '" data-plug="' . $slots['plug'] . '" rowspan="' . $modulelenght . '" style="width: ' . $width . '; background-color: ' . $color . ';border-color: #797979 !important; max-width: ' . $maxwidth . 'px;"><span></span> ' . $slots['modulname'] . $extTextspan . '</td>';
$position++;
$calcwidth = $calcwidth + $slots['width'];
}
if ($calcwidth != 12) {
echo '<td colspan="' . $slots['width'] . '" style="width: ' . $width . ';" class="text-center border-top border-bottom border-right pl-1 pr-1" rowspan="' . $modulelenght . '">frei</td>';
$calcwidth = $calcwidth + $slots['width'];
}
if ($calcwidth != 12) {
echo '<td colspan="' . $slots['width'] . '" style="width: ' . $width . ';" class="text-center border-top border-bottom border-right pl-1 pr-1" rowspan="' . $modulelenght . '">frei</td>';
$calcwidth = $calcwidth + $slots['width'];
}
if ($calcwidth != 12) {
echo '<td colspan="' . $slots['width'] . '" style="width: ' . $width . ';" class="text-center border-top border-bottom border-right pl-1 pr-1" rowspan="' . $modulelenght . '">frei</td>';
$calcwidth = $calcwidth + $slots['width'];
}
$blocktd = $modulelenght + $i;
}
}
if ($i >= $blocktd) {
echo '<td colspan="12" style="background-color: #fff;" class="text-center he-free" ><span></span> frei</td>';
}
?>
</tr>
<?php endfor; ?>
</tbody> </tbody>
</table> </table>
@@ -627,6 +719,158 @@ if (!empty(trim($pops->vlan_ipv6)))
</div> </div>
<?php endif; ?> <?php endif; ?>
<div class="modal fade" id="cableModal" tabindex="-1" role="dialog" aria-labelledby="cableModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="cableModalLabel">Kabel auf Patch Panel legen</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="card">
<div class="card-body">
<input type="hidden" id="cable-poprackmodule-id" name="poprackmodule_id">
<div class="form-group row">
<div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="cable-name">Kabel-Name*</label>
<div class="col-lg-6">
<input required="required" type="text" id="cable-name" name="cable-name"
class="form-control"
placeholder="z.B. K001"/>
</div>
</div>
<div class="form-group row">
<div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="port-start">Start-Port*</label>
<div class="col-lg-3">
<input required="required" type="number" min="1" id="port-start" name="port-start"
class="form-control"/>
</div>
</div>
<div class="form-group row">
<div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="port-end">End-Port*</label>
<div class="col-lg-3">
<input required="required" type="number" min="1" id="port-end" name="port-end"
class="form-control"/>
</div>
</div>
<div class="form-group row">
<div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="fiber-start">Faser von</label>
<div class="col-lg-3">
<input type="number" min="1" id="fiber-start" name="fiber-start" class="form-control"/>
</div>
</div>
<div class="form-group row">
<div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="fiber-end">Faser bis</label>
<div class="col-lg-3">
<input type="number" min="1" id="fiber-end" name="fiber-end" class="form-control"/>
</div>
</div>
<div class="form-group row">
<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>
</div>
</div>
<div class="alert alert-danger text-center" role="alert" style="display: none"
id="cable-modal-alert"></div>
</div>
</div>
</div>
<div class="modal-footer">
<button id="cable-add-button" type="button" class="btn btn-primary">Kabel hinzufügen</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Abbrechen</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="cableEditModal" tabindex="-1" role="dialog" aria-labelledby="cableEditModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="cableEditModalLabel">Kabel bearbeiten</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
<div class="card">
<div class="card-body">
<input type="hidden" id="edit-cable-id">
<div class="form-group row">
<div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="edit-cable-name">Kabel-Name*</label>
<div class="col-lg-6">
<input required="required" type="text" id="edit-cable-name" class="form-control"/>
</div>
</div>
<div class="form-group row">
<div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="edit-port-start">Start-Port*</label>
<div class="col-lg-3">
<input required="required" type="number" min="1" id="edit-port-start"
class="form-control"/>
</div>
</div>
<div class="form-group row">
<div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="edit-port-end">End-Port*</label>
<div class="col-lg-3">
<input required="required" type="number" min="1" id="edit-port-end"
class="form-control"/>
</div>
</div>
<div class="form-group row">
<div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="edit-fiber-start">Faser von</label>
<div class="col-lg-3">
<input type="number" min="1" id="edit-fiber-start" name="fiber-start"
class="form-control"/>
</div>
</div>
<div class="form-group row">
<div class="col-lg-1"></div>
<label class="col-lg-4 col-form-label" for="edit-fiber-end">Faser bis</label>
<div class="col-lg-3">
<input type="number" min="1" id="edit-fiber-end" name="fiber-end" class="form-control"/>
</div>
</div>
<div class="form-group row">
<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>
</div>
</div>
<div class="alert alert-danger text-center" role="alert" style="display: none"
id="cable-edit-modal-alert"></div>
</div>
</div>
</div>
<div class="modal-footer">
<button id="cable-update-button" type="button" class="btn btn-primary">Änderungen speichern</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Abbrechen</button>
</div>
</div>
</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>
<a class="dropdown-item" href="#" data-action="edit"><span class="context-span"><i
class="fas fa-edit fa-fw"></i></span> Bearbeiten</a>
<a class="dropdown-item" href="#" data-action="delete"><span class="context-span"><i class="fas fa-trash fa-fw"></i></span>
Löschen</a>
</div>
<script type="text/javascript" src="<?= self::getResourcePath() ?>assets/js/Sortable.js?<?= $git_merge_ts ?>"></script> <script type="text/javascript" src="<?= self::getResourcePath() ?>assets/js/Sortable.js?<?= $git_merge_ts ?>"></script>
<script type="text/javascript"> <script type="text/javascript">
@@ -640,8 +884,13 @@ if (!empty(trim($pops->vlan_ipv6)))
let linkEditRack = "<?= self::getUrl("Poprack", "api", ['do' => 'editRack']) ?>"; let linkEditRack = "<?= self::getUrl("Poprack", "api", ['do' => 'editRack']) ?>";
let linkAddRack = "<?= self::getUrl("Poprack", "api", ['do' => 'addRack']) ?>"; let linkAddRack = "<?= self::getUrl("Poprack", "api", ['do' => 'addRack']) ?>";
let linkRemoveRack = "<?= self::getUrl("Poprack", "api", ['do' => 'removeRack']) ?>"; let linkRemoveRack = "<?= self::getUrl("Poprack", "api", ['do' => 'removeRack']) ?>";
let linkAddCable = "<?= self::getUrl("Poprackmodulecable", "api", ['do' => 'addCable']) ?>";
let linkRemoveCable = "<?= self::getUrl("Poprackmodulecable", "api", ['do' => 'removeCable']) ?>";
let linkUpdateCable = "<?= self::getUrl("Poprackmodulecable", "api", ['do' => 'updateCable']) ?>";
</script> </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/detail.js?<?= $git_merge_ts ?>"></script>
<script type="text/javascript" src="<?= self::getResourcePath() ?>assets/js/datatables-std.js?<?= $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"); ?> <?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>

View File

@@ -0,0 +1,140 @@
<?php
$cellwidth = 227;
$blocktd = 0;
for ($i = 1; $i <= $rack_he; $i++) : ?>
<tr>
<td class="border-right w-10 p-0 pl-1 pr-1 border-bottom font-13 rack-he user-select-none"
data-toggle="modal" data-target="#rackModuleModal"
style="cursor: pointer" data-he="<?= $i; ?>">He<?= $i; ?></td>
<?php
foreach ($modules_to_render as $module) {
if ($module['start_he'] == $i) {
$modulestart = 1;
$modulelenght = $module['end_he'] - $module['start_he'] + 1;
$position = 1;
$slotcounter = 0;
$calcwidth = 0;
$extText = "";
$extTextspan = "";
foreach ($module['slots'] as $slots) {
$extText = "";
$originalName = $slots['modulname'];
$displayName = $originalName;
$colorclass = "";
if ($slots['type'] == '0' && empty(trim($displayName))) {
$displayName = "LWL Patch Panel";
}
if ($slots['type'] == '0') {
if (isset($slots['status']) && $slots['status'] == 'planned') {
$colorclass = "rack-color-lwl-planned";
} else {
$colorclass = "rack-color-lwl";
}
$cellContent = "";
$plugName = isset($plugs[$slots['plug']]) ? $plugs[$slots['plug']] : 'k.A.';
$cellContent .= '<div class="module-header-info">';
$cellContent .= ' <span class="module-name" title="' . $displayName . '">' . $displayName . '</span>';
$cellContent .= ' <span class="port-info">(' . $slots['ports'] . 'x' . $plugName . ')</span>';
$cellContent .= '</div>';
$cellContent .= '<div class="cable-container" style="background-color: #fffbe9;">';
if (!empty($slots['cables'])) {
usort($slots['cables'], function ($a, $b) {
return $a['port_start'] <=> $b['port_start'];
});
$lastPort = 0;
foreach ($slots['cables'] as $cable) {
$freePorts = $cable['port_start'] - $lastPort - 1;
if ($freePorts > 0) {
$freeWidth = ($freePorts / $slots['ports']) * 100;
$cellContent .= '<div class="cable-free" style="width: ' . $freeWidth . '%;" title="Ports ' . ($lastPort + 1) . '-' . ($cable['port_start'] - 1) . ' frei">Ports ' . ($lastPort + 1) . '-' . ($cable['port_start'] - 1) . ' frei</div>';
}
$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);
}
$cellContent .= '<div class="cable-item" title="' . $cableTitle . '" 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>";
if (!empty($cable['description'])) {
$cellContent .= '<span class="cable-description" >' . 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>';
if (!empty($cable['fiber_start'])) {
$cellContent .= '<tool class="fiber-range">F: ' . $cable['fiber_start'] . '-' . $cable['fiber_end'] . "</tool>";
}
$cellContent .= '</div>';
$cellContent .= '</div>';
$lastPort = $cable['port_end'];
}
$remainingPorts = $slots['ports'] - $lastPort;
if ($remainingPorts > 0) {
$remainingWidth = ($remainingPorts / $slots['ports']) * 100;
$cellContent .= '<div class="cable-free" style="width: ' . $remainingWidth . '%;" title="Ports ' . ($lastPort + 1) . '-' . $slots['ports'] . ' frei">P: ' . ($lastPort + 1) . '-' . $slots['ports'] . ' frei</div>';
}
} else {
$cellContent .= '<span style="font-size: 12px; font-style: italic; margin: auto;" class="cable-free">Ports ' . '1 -' . $slots['ports'] . ' frei</span>';
}
$cellContent .= '</div>';
$slots['modulname'] = $cellContent;
$extTextspan = "";
} else if ($slots['type'] == '1') {
$slots['modulname'] = '<a style="color: #000000;text-decoration: underline;" target="_self" href="' . self::getUrl("Device", "detail", ["id" => $slots['device_id']]) . '">' . $displayName . '</a>';
$colorclass = "rack-color-device";
} else if ($slots['type'] == '2') {
$colorclass = "rack-color-rpanel";
} else if ($slots['type'] == '3') {
$colorclass = "rack-color-infra";
} else if ($slots['type'] == '4') {
$colorclass = "rack-color-panel";
} else if ($slots['type'] == '5') {
$colorclass = "rack-color-blocked";
}
if ($slots['width'] == "12") $width = "85%";
else if ($slots['width'] == "6") $width = "42%";
else if ($slots['width'] == "4") $width = "28%";
else if ($slots['width'] == "3") $width = "21%";
while ($position < $slots['position']) {
echo '<td colspan="' . $slots['width'] . '" class="text-center border-top border-bottom border-right pl-1 pr-1" rowspan="' . $modulelenght . '">frei</td>';
$position++;
$calcwidth += $slots['width'];
}
echo '<td title="' . strip_tags($displayName) . $extText . '" colspan="' . $slots['width'] . '" class="text-center border-top border-bottom border-right ' . $colorclass . '" data-id="' . $slots['moduleid'] . '" data-type="' . $slots['type'] . '" data-name="' . strip_tags($originalName) . '" data-status="' . ($slots['status'] ?? 'productive') . '" data-starthe="' . $module['start_he'] . '" data-ports="' . $slots['ports'] . '" data-plug="' . $slots['plug'] . '" rowspan="' . $modulelenght . '" style="width: ' . $width . '; border-color: #797979 !important;">' . $slots['modulname'] . $extTextspan . '</td>';
$position++;
$calcwidth += $slots['width'];
}
if ($calcwidth < 12) {
$remaining_width = 12 - $calcwidth;
echo '<td colspan="' . $remaining_width . '" class="text-center border-top border-bottom border-right pl-1 pr-1" rowspan="' . $modulelenght . '">frei</td>';
}
$blocktd = $modulelenght + $i;
}
}
if ($i >= $blocktd) {
echo '<td colspan="12" style="background-color: #fff;" class="text-center he-free pt-1 pb-1"><span></span> frei</td>';
}
?>
</tr>
<?php endfor; ?>

View File

@@ -21,9 +21,20 @@ class PoprackController extends mfBaseController
case "sortRack": case "sortRack":
$return = $this->sortRack(); $return = $this->sortRack();
break; break;
case "generateRack"; case "generateRack":
$return = $this->generateRack(); $rack_id = $this->request->id;
break; $side = $this->request->side ?? 'front';
$rackDataArray = PoprackModel::getAllbyRack($rack_id);
$rackData = $rackDataArray[0] ?? null;
if ($rackData) {
$modules_to_render = $rackData['modules'][$side] ?? [];
$rack_he = $rackData['rack']['he'];
$plugs[1] = "LC/APC";
$plugs[2] = "SC/APC";
$plugs[3] = "E2000/APC";
include(dirname(__FILE__) . "/../../Layout/default/Pop/_rack_body.php");
}
exit;
default: default:
$return = false; $return = false;
} }

View File

@@ -42,28 +42,11 @@ class PoprackModel
return $model; return $model;
} }
private static function _processRackData($res)
public static function getAllbyPop($pop_id)
{ {
$items = []; $items = [];
$db = FronkDB::singleton(); $db = FronkDB::singleton();
$sql = "(SELECT `Poprackmodule`.`type`,`Poprack`.`id`,`Poprack`.`sort`, `Poprack`.`name`, `Poprack`.`he`,`Poprackmodule`.start_he,`Poprackmodule`.end_he,`Poprackmodule`.`name` as `modulname`,device_id ,`Poprackmodule`.`id` as moduleid,`Poprackmodule`.`width` as modulewidth,`Poprackmodule`.`position` as moduleposition,`Poprackmodule`.`side` as moduleside,`Poprackmodule`.`ports` as moduleports,`Poprackmodule`.`plug` as moduleplug FROM `Poprack`
LEFT JOIN `Poprackmodule` ON (`Poprackmodule`.`poprack_id`=`Poprack`.`id` AND `Poprackmodule`.`type`!=1)
WHERE `Poprack`.`pop_id`='" . $pop_id . "' ORDER by `Poprack`.`sort`,`Poprack`.`name`,`Poprackmodule`.start_he,`Poprackmodule`.position)
UNION
(SELECT `Poprackmodule`.`type`,`Poprack`.`id`,`Poprack`.`sort`, `Poprack`.`name`, `Poprack`.`he`,`Poprackmodule`.start_he,`Poprackmodule`.end_he,`Device`.`name` as `modulname`,device_id,`Poprackmodule`.`id` as moduleid,`Poprackmodule`.`width` as modulewidth,`Poprackmodule`.`position` as moduleposition,`Poprackmodule`.`side` as moduleside,`Poprackmodule`.`ports` as moduleports,`Poprackmodule`.`plug` as moduleplug FROM `Poprack`
LEFT JOIN `Poprackmodule` ON (`Poprackmodule`.`poprack_id`=`Poprack`.`id` AND `Poprackmodule`.`type`=1)
LEFT JOIN `Device` ON (`Device`.`id`=`Poprackmodule`.`device_id`)
WHERE `Poprack`.`pop_id`='" . $pop_id . "' ORDER by `Poprack`.`sort`,`Poprack`.`name`,`Poprackmodule`.start_he,`Poprackmodule`.position)
ORDER BY sort,name,start_he,moduleposition
";
//
// $sql = "SELECT `Poprack`.`id`, `Poprack`.`name`, `Poprack`.`he`,`Poprackmodule`.start_he,`Poprackmodule`.end_he,`Poprackmodule`.`name` as `modulname` FROM `Poprack`
// LEFT JOIN `Poprackmodule` ON (`Poprackmodule`.`poprack_id`=`Poprack`.`id`)
// WHERE `Poprack`.`pop_id`='" . $pop_id . "' ORDER by `Poprack`.`sort`,`Poprack`.`name`,`Poprackmodule`.`start_he`";
$res = $db->query($sql);
if ($db->num_rows($res)) { if ($db->num_rows($res)) {
$oldrackid = ""; $oldrackid = "";
$counter = -1; $counter = -1;
@@ -73,7 +56,6 @@ class PoprackModel
$items[$counter]['rack']['id'] = $data['id']; $items[$counter]['rack']['id'] = $data['id'];
$items[$counter]['rack']['name'] = $data['name']; $items[$counter]['rack']['name'] = $data['name'];
$items[$counter]['rack']['he'] = $data['he']; $items[$counter]['rack']['he'] = $data['he'];
$modulecounter = 0;
} }
// if ($data['modulname']) { // if ($data['modulname']) {
if ($data['device_id']) { if ($data['device_id']) {
@@ -88,71 +70,57 @@ class PoprackModel
$items[$counter]['modules'][$data['moduleside']][$data['start_he']]['slots'][$data['moduleid']]['position'] = $data['moduleposition']; $items[$counter]['modules'][$data['moduleside']][$data['start_he']]['slots'][$data['moduleid']]['position'] = $data['moduleposition'];
$items[$counter]['modules'][$data['moduleside']][$data['start_he']]['slots'][$data['moduleid']]['ports'] = $data['moduleports']; $items[$counter]['modules'][$data['moduleside']][$data['start_he']]['slots'][$data['moduleid']]['ports'] = $data['moduleports'];
$items[$counter]['modules'][$data['moduleside']][$data['start_he']]['slots'][$data['moduleid']]['plug'] = $data['moduleplug']; $items[$counter]['modules'][$data['moduleside']][$data['start_he']]['slots'][$data['moduleid']]['plug'] = $data['moduleplug'];
$modulecounter++; $items[$counter]['modules'][$data['moduleside']][$data['start_he']]['slots'][$data['moduleid']]['status'] = $data['status'];
if ($data['type'] == 0) {
$cables = PoprackmodulecableModel::getForModule($data['moduleid']);
$cableData = [];
foreach ($cables as $cable) {
$cleanCable = (array)$cable->data;
$cleanCable['id'] = $cable->id;
$cableData[] = $cleanCable;
} }
$items[$counter]['modules'][$data['moduleside']][$data['start_he']]['slots'][$data['moduleid']]['cables'] = $cableData;
}
// }
$oldrackid = $data['id']; $oldrackid = $data['id'];
} }
} }
return $items; return $items;
}
public static function getAllbyPop($pop_id)
{
$db = FronkDB::singleton();
$sql = "(SELECT `Poprackmodule`.`type`,`Poprack`.`id`,`Poprack`.`sort`, `Poprack`.`name`, `Poprack`.`he`,`Poprackmodule`.start_he,`Poprackmodule`.end_he,`Poprackmodule`.`name` as `modulname`,device_id ,`Poprackmodule`.`id` as moduleid,`Poprackmodule`.`width` as modulewidth,`Poprackmodule`.`position` as moduleposition,`Poprackmodule`.`side` as moduleside,`Poprackmodule`.`ports` as moduleports,`Poprackmodule`.`plug` as moduleplug,Poprackmodule.status FROM `Poprack`
LEFT JOIN `Poprackmodule` ON (`Poprackmodule`.`poprack_id`=`Poprack`.`id` AND `Poprackmodule`.`type`!=1)
WHERE `Poprack`.`pop_id`='" . $pop_id . "' ORDER by `Poprack`.`sort`,`Poprack`.`name`,`Poprackmodule`.start_he,`Poprackmodule`.position)
UNION
(SELECT `Poprackmodule`.`type`,`Poprack`.`id`,`Poprack`.`sort`, `Poprack`.`name`, `Poprack`.`he`,`Poprackmodule`.start_he,`Poprackmodule`.end_he,`Device`.`name` as `modulname`,device_id,`Poprackmodule`.`id` as moduleid,`Poprackmodule`.`width` as modulewidth,`Poprackmodule`.`position` as moduleposition,`Poprackmodule`.`side` as moduleside,`Poprackmodule`.`ports` as moduleports,`Poprackmodule`.`plug` as moduleplug,Poprackmodule.status FROM `Poprack`
LEFT JOIN `Poprackmodule` ON (`Poprackmodule`.`poprack_id`=`Poprack`.`id` AND `Poprackmodule`.`type`=1)
LEFT JOIN `Device` ON (`Device`.`id`=`Poprackmodule`.`device_id`)
WHERE `Poprack`.`pop_id`='" . $pop_id . "' ORDER by `Poprack`.`sort`,`Poprack`.`name`,`Poprackmodule`.start_he,`Poprackmodule`.position)
ORDER BY sort,name,start_he,moduleposition
";
$res = $db->query($sql);
return self::_processRackData($res);
} }
public static function getAllbyRack($rack_id) public static function getAllbyRack($rack_id)
{ {
$items = [];
$db = FronkDB::singleton(); $db = FronkDB::singleton();
$sql = "(SELECT `Poprackmodule`.`type`,`Poprack`.`id`,`Poprack`.`sort`, `Poprack`.`name`, `Poprack`.`he`,`Poprackmodule`.start_he,`Poprackmodule`.end_he,`Poprackmodule`.`name` as `modulname`,device_id ,`Poprackmodule`.`id` as moduleid,`Poprackmodule`.`width` as modulewidth,`Poprackmodule`.`position` as moduleposition,`Poprackmodule`.`side` as moduleside,`Poprackmodule`.`ports` as moduleports,`Poprackmodule`.`plug` as moduleplug FROM `Poprack` $sql = "(SELECT `Poprackmodule`.`type`,`Poprack`.`id`,`Poprack`.`sort`, `Poprack`.`name`, `Poprack`.`he`,`Poprackmodule`.start_he,`Poprackmodule`.end_he,`Poprackmodule`.`name` as `modulname`,device_id ,`Poprackmodule`.`id` as moduleid,`Poprackmodule`.`width` as modulewidth,`Poprackmodule`.`position` as moduleposition,`Poprackmodule`.`side` as moduleside,`Poprackmodule`.`ports` as moduleports,`Poprackmodule`.`plug` as moduleplug,Poprackmodule.status FROM `Poprack`
LEFT JOIN `Poprackmodule` ON (`Poprackmodule`.`poprack_id`=`Poprack`.`id` AND `Poprackmodule`.`type`!=1) LEFT JOIN `Poprackmodule` ON (`Poprackmodule`.`poprack_id`=`Poprack`.`id` AND `Poprackmodule`.`type`!=1)
WHERE `Poprack`.`id`='" . $rack_id . "' ORDER by `Poprack`.`sort`,`Poprack`.`name`,`Poprackmodule`.start_he,`Poprackmodule`.position) WHERE `Poprack`.`id`='" . $rack_id . "' ORDER by `Poprack`.`sort`,`Poprack`.`name`,`Poprackmodule`.start_he,`Poprackmodule`.position)
UNION UNION
(SELECT `Poprackmodule`.`type`,`Poprack`.`id`,`Poprack`.`sort`, `Poprack`.`name`, `Poprack`.`he`,`Poprackmodule`.start_he,`Poprackmodule`.end_he,`Device`.`name` as `modulname`,device_id,`Poprackmodule`.`id` as moduleid,`Poprackmodule`.`width` as modulewidth,`Poprackmodule`.`position` as moduleposition,`Poprackmodule`.`side` as moduleside,`Poprackmodule`.`ports` as moduleports,`Poprackmodule`.`plug` as moduleplug FROM `Poprack` (SELECT `Poprackmodule`.`type`,`Poprack`.`id`,`Poprack`.`sort`, `Poprack`.`name`, `Poprack`.`he`,`Poprackmodule`.start_he,`Poprackmodule`.end_he,`Device`.`name` as `modulname`,device_id,`Poprackmodule`.`id` as moduleid,`Poprackmodule`.`width` as modulewidth,`Poprackmodule`.`position` as moduleposition,`Poprackmodule`.`side` as moduleside,`Poprackmodule`.`ports` as moduleports,`Poprackmodule`.`plug` as moduleplug ,Poprackmodule.status FROM `Poprack`
LEFT JOIN `Poprackmodule` ON (`Poprackmodule`.`poprack_id`=`Poprack`.`id` AND `Poprackmodule`.`type`=1) LEFT JOIN `Poprackmodule` ON (`Poprackmodule`.`poprack_id`=`Poprack`.`id` AND `Poprackmodule`.`type`=1)
LEFT JOIN `Device` ON (`Device`.`id`=`Poprackmodule`.`device_id`) LEFT JOIN `Device` ON (`Device`.`id`=`Poprackmodule`.`device_id`)
WHERE `Poprack`.`id`='" . $rack_id . "' ORDER by `Poprack`.`sort`,`Poprack`.`name`,`Poprackmodule`.start_he,`Poprackmodule`.position) WHERE `Poprack`.`id`='" . $rack_id . "' ORDER by `Poprack`.`sort`,`Poprack`.`name`,`Poprackmodule`.start_he,`Poprackmodule`.position)
ORDER BY sort,name,start_he,moduleposition ORDER BY sort,name,start_he,moduleposition
"; ";
//
// $sql = "SELECT `Poprack`.`id`, `Poprack`.`name`, `Poprack`.`he`,`Poprackmodule`.start_he,`Poprackmodule`.end_he,`Poprackmodule`.`name` as `modulname` FROM `Poprack`
// LEFT JOIN `Poprackmodule` ON (`Poprackmodule`.`poprack_id`=`Poprack`.`id`)
// WHERE `Poprack`.`pop_id`='" . $pop_id . "' ORDER by `Poprack`.`sort`,`Poprack`.`name`,`Poprackmodule`.`start_he`";
$res = $db->query($sql); $res = $db->query($sql);
if ($db->num_rows($res)) { return self::_processRackData($res);
$oldrackid = "";
$counter = -1;
while ($data = $db->fetch_array($res)) {
if ($oldrackid != $data['id']) {
$counter++;
$items[$counter]['rack']['id'] = $data['id'];
$items[$counter]['rack']['name'] = $data['name'];
$items[$counter]['rack']['he'] = $data['he'];
$modulecounter = 0;
}
if ($data['modulname']) {
if ($data['device_id']) {
$items[$counter]['modules'][$data['start_he']]['slots'][$data['moduleid']]['device_id'] = $data['device_id'];
}
$items[$counter][$data['moduleside']]['modules'][$data['start_he']]['start_he'] = $data['start_he'];
$items[$counter][$data['moduleside']]['modules'][$data['start_he']]['end_he'] = $data['end_he'];
$items[$counter][$data['moduleside']]['modules'][$data['start_he']]['slots'][$data['moduleid']]['modulname'] = $data['modulname'];
$items[$counter][$data['moduleside']]['modules'][$data['start_he']]['slots'][$data['moduleid']]['moduleid'] = $data['moduleid'];
$items[$counter][$data['moduleside']]['modules'][$data['start_he']]['slots'][$data['moduleid']]['type'] = $data['type'];
$items[$counter][$data['moduleside']]['modules'][$data['start_he']]['slots'][$data['moduleid']]['width'] = $data['modulewidth'];
$items[$counter][$data['moduleside']]['modules'][$data['start_he']]['slots'][$data['moduleid']]['position'] = $data['moduleposition'];
$items[$counter][$data['moduleside']]['modules'][$data['start_he']]['slots'][$data['moduleid']]['ports'] = $data['moduleports'];
$items[$counter][$data['moduleside']]['modules'][$data['start_he']]['slots'][$data['moduleid']]['plug'] = $data['moduleplug'];
$modulecounter++;
}
$oldrackid = $data['id'];
}
}
return $items;
} }
public static function getpoprack($pop_id) public static function getpoprack($pop_id)

View File

@@ -21,7 +21,6 @@ class PoprackmoduleController extends mfBaseController
default: default:
$return = false; $return = false;
} }
if (!is_array($return) || !count($return)) { if (!is_array($return) || !count($return)) {
$data = ["status" => "error"]; $data = ["status" => "error"];
$this->returnJson($data); $this->returnJson($data);
@@ -54,7 +53,7 @@ class PoprackmoduleController extends mfBaseController
$data['width'] = $r->width; $data['width'] = $r->width;
$data['position'] = ($r->position) ? $r->position : null; $data['position'] = ($r->position) ? $r->position : null;
$data['side'] = ($r->side) ? $r->side : 'front'; $data['side'] = ($r->side) ? $r->side : 'front';
$data['status'] = ($r->status) ? $r->status : 'productive';
$poprackmodule = PoprackmoduleModel::create($data); $poprackmodule = PoprackmoduleModel::create($data);
$new_id = $poprackmodule->save(); $new_id = $poprackmodule->save();
@@ -119,6 +118,7 @@ class PoprackmoduleController extends mfBaseController
$data['type'] = $r->type; $data['type'] = $r->type;
} }
$data['name'] = ($r->name); $data['name'] = ($r->name);
$data['status'] = ($r->status);
$poprackmodule->update($data); $poprackmodule->update($data);
$new_id = $poprackmodule->save(); $new_id = $poprackmodule->save();
if (!$new_id) { if (!$new_id) {

View File

@@ -14,6 +14,7 @@ class PoprackmoduleModel
public $plug = null; public $plug = null;
public $position = null; public $position = null;
public $side = null; public $side = null;
public $status = null;
public $create_by = null; public $create_by = null;
public $edit_by = null; public $edit_by = null;
public $create = null; public $create = null;
@@ -99,5 +100,4 @@ class PoprackmoduleModel
exit; exit;
} }
} }

View File

@@ -0,0 +1,60 @@
<?php
class Poprackmodulecable extends mfBaseModel
{
private $editor;
private $creator;
private $poprackmodule;
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;
}
$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;
}
}

View File

@@ -0,0 +1,283 @@
<?php
class PoprackmodulecableController 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 apiAction()
{
$do = $this->request->do;
$me = new User(); // Annahme, dass der User geladen werden muss für create_by etc.
$me->loadMe();
switch ($do) {
case "addCable":
$data = [
'poprackmodule_id' => $this->request->poprackmodule_id,
'cable_name' => trim($this->request->cable_name),
'port_start' => (int)$this->request->port_start,
'port_end' => (int)$this->request->port_end,
'fiber_start' => ($this->request->fiber_start) ? (int)$this->request->fiber_start : null,
'fiber_end' => ($this->request->fiber_end) ? (int)$this->request->fiber_end : null,
'description' => trim($this->request->description) ?: null,
'create_by' => $this->me->id,
'edit_by' => $this->me->id,
];
$module = new Poprackmodule($data['poprackmodule_id']);
if (!$module->id) {
$this->returnJson(['success' => false, 'error' => 'Modul nicht gefunden.']);
return;
}
if (empty($data['cable_name']) || empty($data['port_start']) || empty($data['port_end'])) {
$this->returnJson(['success' => false, 'error' => 'Alle Felder sind Pflichtfelder.']);
return;
}
if ($data['port_start'] > $data['port_end'] || $data['port_start'] < 1 || $data['port_end'] > $module->ports) {
$this->returnJson(['success' => false, 'error' => 'Ungültiger Port-Bereich. Ports müssen zwischen 1 und ' . $module->ports . ' liegen.']);
return;
}
if ($data['fiber_start'] && $data['fiber_end']) {
if ($data['fiber_start'] > $data['fiber_end']) {
$this->returnJson(['success' => false, 'error' => 'Faser-Start muss kleiner oder gleich Faser-Ende sein.']);
return;
}
$portCount = $data['port_end'] - $data['port_start'] + 1;
$fiberCount = $data['fiber_end'] - $data['fiber_start'] + 1;
if ($fiberCount > $portCount) {
$this->returnJson(['success' => false, 'error' => 'Anzahl der Fasern (' . $fiberCount . ') darf nicht größer als die der Ports (' . $portCount . ') sein.']);
return;
}
}
$existingCables = PoprackmodulecableModel::getForModule($data['poprackmodule_id']);
$newStart = $data['port_start'];
$newEnd = $data['port_end'];
foreach ($existingCables as $existingCable) {
// Die Formel zur Prüfung auf Überlappung lautet: (StartA <= EndeB) und (EndeA >= StartB)
if ($newStart <= $existingCable->port_end && $newEnd >= $existingCable->port_start) {
$this->returnJson([
'success' => false,
'error' => 'Port-Bereich (' . $newStart . '-' . $newEnd . ') kollidiert mit Kabel "' . $existingCable->cable_name . '" (' . $existingCable->port_start . '-' . $existingCable->port_end . ').'
]);
return; // Wichtig: Abbruch, wenn eine Kollision gefunden wurde
}
}
$cable = PoprackmodulecableModel::create($data);
$newId = $cable->save();
$this->returnJson(['success' => (bool)$newId]);
break;
case "updateCable":
$cable_id = $this->request->id;
$cable = new Poprackmodulecable($cable_id);
if (!$cable->id) {
$this->returnJson(['success' => false, 'error' => 'Kabel nicht gefunden']);
return;
}
$data = [
'cable_name' => trim($this->request->cable_name),
'port_start' => (int)$this->request->port_start,
'port_end' => (int)$this->request->port_end,
'fiber_start' => ($this->request->fiber_start) ? (int)$this->request->fiber_start : null,
'fiber_end' => ($this->request->fiber_end) ? (int)$this->request->fiber_end : null,
'description' => trim($this->request->description) ?: null, // NEU
'edit_by' => $this->me->id,
];
$module = new Poprackmodule($cable->poprackmodule_id);
if (!$module->id) {
$this->returnJson(['success' => false, 'error' => 'Zugehöriges Modul nicht gefunden.']);
return;
}
if (empty($data['cable_name']) || empty($data['port_start']) || empty($data['port_end'])) {
$this->returnJson(['success' => false, 'error' => 'Alle Felder sind Pflichtfelder.']);
return;
}
if ($data['port_start'] > $data['port_end'] || $data['port_start'] < 1 || $data['port_end'] > $module->ports) {
$this->returnJson(['success' => false, 'error' => 'Ungültiger Port-Bereich. Ports müssen zwischen 1 und ' . $module->ports . ' liegen.']);
return;
}
if (($data['fiber_start'] && $data['fiber_end'])) {
if ($data['fiber_start'] > $data['fiber_end']) {
$this->returnJson(['success' => false, 'error' => 'Faser-Start muss kleiner oder gleich Faser-Ende sein.']);
return;
}
$portCount = $data['port_end'] - $data['port_start'] + 1;
$fiberCount = $data['fiber_end'] - $data['fiber_start'] + 1;
if ($fiberCount > $portCount) {
$this->returnJson(['success' => false, 'error' => 'Anzahl der Fasern (' . $fiberCount . ') darf nicht größer als die der Ports (' . $portCount . ') sein.']);
return;
}
}
$existingCables = PoprackmodulecableModel::getForModule($cable->poprackmodule_id);
$newStart = $data['port_start'];
$newEnd = $data['port_end'];
foreach ($existingCables as $existingCable) {
if ($existingCable->id == $cable_id) {
continue;
}
if ($newStart <= $existingCable->port_end && $newEnd >= $existingCable->port_start) {
$this->returnJson([
'success' => false,
'error' => 'Port-Bereich (' . $newStart . '-' . $newEnd . ') kollidiert mit Kabel "' . $existingCable->cable_name . '" (' . $existingCable->port_start . '-' . $existingCable->port_end . ').'
]);
return;
}
}
$cable->update($data);
$cable->save();
$this->returnJson(['success' => true]);
break;
case "removeCable":
$cable_id = $this->request->id;
if (!is_numeric($cable_id)) {
$this->returnJson(['success' => false, 'error' => 'Ungültige ID.']);
return;
}
$cable = new Poprackmodulecable($cable_id);
if ($cable->id) {
$cable->delete();
$this->returnJson(['success' => true]);
} else {
$this->returnJson(['success' => false, 'error' => 'Kabel nicht gefunden']);
}
break;
default:
$this->returnJson(['success' => false, 'error' => 'Unbekannte Aktion']);
break;
}
}
protected function indexAction()
{
$this->layout()->setTemplate("Poprackmodulecable/Index");
$poprackmodulecables = PoprackmodulecableModel::getAll();
$this->layout()->set("poprackmodulecables", $poprackmodulecables);
}
protected function addAction()
{
$poprackmodules=PoprackmoduleModel::getAll();
$this->layout()->set("poprackmodules", $poprackmodules);
$this->layout()->setTemplate("Poprackmodulecable/Form");
}
protected function editAction()
{
$id = $this->request->id;
if (!is_numeric($id) || !$id) {
$this->layout()->setFlash("dfsdf nicht gefunden", "error");
$this->redirect("Poprackmodulecable");
}
$poprackmodulecables = new Poprackmodulecable($id);
if ($poprackmodulecables->id != $id) {
$this->layout()->setFlash("dfsdf nicht gefunden", "error");
$this->redirect("Poprackmodulecable");
}
$this->layout()->set("poprackmodulecables", $poprackmodulecables);
return $this->addAction();
}
protected function saveAction()
{
$r = $this->request;
$id = $r->id;
//var_dump($r->get());exit;
if (is_numeric($id) && $id > 0) {
$mode = "edit";
$poprackmodulecables = new Poprackmodulecable($id);
if (!$poprackmodulecables->id) {
$this->layout()->setFlash("dfsdf nicht gefunden", "error");
$this->redirect("Poprackmodulecable");
}
} else {
$mode = "add";
}
$data = [];
$data['poprackmodule_id'] = trim($r->poprackmodule_id);
$data['cable_name'] = trim($r->cable_name);
$data['port_start'] = trim($r->port_start);
$data['port_end'] = trim($r->port_end);
if (!$data['poprackmodule_id']) {
$data['poprackmodule_id']=NULL;
}
if (!$data['cable_name']) {
$data['cable_name']=NULL;
}
if (!$data['port_start']) {
$data['port_start']=NULL;
}
if (!$data['port_end']) {
$data['port_end']=NULL;
}
// var_dump($_FILES);
// var_dump($upload);
// exit;
if ($mode == "edit") {
$poprackmodulecables->update($data);
} else {
$poprackmodulecables = PoprackmodulecableModel::create($data);
}
// var_dump($filestore);
// exit;
$id = $poprackmodulecables->save();
if (!$id) {
$this->layout()->setFlash("dfsdf konnte nicht angelegt werden", "error");
$this->redirect("Poprackmodulecable");
}
if ($mode == "edit") {
$this->layout()->setFlash("dfsdf erfolgreich geändert", "success");
} else if ($mode = "add") {
$this->layout()->setFlash("dfsdf erfolgreich angelegt", "success");
}
$this->redirect("Poprackmodulecable");
}
protected function deleteAction()
{
$id = $this->request->id;
$poprackmodulecables = new Poprackmodulecable($id);
if (!$poprackmodulecables->id || $poprackmodulecables->id != $id) {
$this->layout()->setFlash("dfsdf nicht gefunden.", "error");
$this->redirect("Poprackmodulecable");
}
$poprackmodulecables->delete();
$this->redirect("Poprackmodulecable");
}
}

View File

@@ -0,0 +1,150 @@
<?php
class PoprackmodulecableModel
{
private $poprackmodule_id;
private $cable_name;
private $port_start;
private $port_end;
private $fiber_start;
private $fiber_end;
private $description;
public static function find($data)
{
}
public static function create(array $data)
{
$model = new Poprackmodulecable();
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 getForModule($poprackmodule_id)
{
$items = [];
if (!is_numeric($poprackmodule_id)) {
return $items;
}
$db = FronkDB::singleton();
$res = $db->select("Poprackmodulecable", "*", "poprackmodule_id = " . (int)$poprackmodule_id . " ORDER BY port_start ASC");
if ($db->num_rows($res)) {
while ($data = $db->fetch_object($res)) {
$items[] = new Poprackmodulecable($data);
}
}
return $items;
}
public static function getOne($id)
{
if (!is_numeric($id) || !$id) {
throw new Exception("Invalid number", 400);
}
$item = [];
$db = FronkDB::singleton();
$res = $db->select("Poprackmodulecable", "*", "id=$id LIMIT 1");
if ($db->num_rows($res)) {
$data = $db->fetch_object($res);
$item = new Poprackmodulecable($data);
}
return $item;
}
public static function getAll()
{
$items = [];
$db = FronkDB::singleton();
$res = $db->select("Poprackmodulecable", "*", "1=1");
if ($db->num_rows($res)) {
while ($data = $db->fetch_object($res)) {
$items[] = new Poprackmodulecable($data);
}
}
return $items;
}
public static function getFirst()
{
$db = FronkDB::singleton();
$where = self::getSqlFilter($filter);
$res = $db->select("Poprackmodulecable", "*", "$where ");
if ($db->num_rows($res)) {
$data = $db->fetch_object($res);
$item = new Poprackmodulecable($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("Poprackmodulecable", "*", "$where");
if ($db->num_rows($res)) {
while ($data = $db->fetch_object($res)) {
$items[] = new Poprackmodulecable($data);
}
}
return $items;
}
private static function getSqlFilter($filter)
{
$where = "1=1 ";
//var_dump($filter);exit;
if (array_key_exists("network_id", $filter)) {
$networkid = $filter['network_id'];
if (is_numeric($networkid)) {
$where .= " AND network_id=$networkid";
}
}
//var_dump($filter, $where);exit;
return $where;
}
}

View File

@@ -132,6 +132,10 @@ $(document).ready(function () {
$('#module-plug').val(parent.find('td').eq(1).data('plug')); $('#module-plug').val(parent.find('td').eq(1).data('plug'));
} }
$('#module-name').val(parent.find('td').eq(1).data('name')); $('#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(); // .change() ist hier wichtig!
$('#module-status').val(status);
$('#module-type option[value="1"]').prop('disabled', true); $('#module-type option[value="1"]').prop('disabled', true);
@@ -217,6 +221,7 @@ $(document).ready(function () {
$('#module-device-id').removeAttr('disabled'); $('#module-device-id').removeAttr('disabled');
$('#module-ports-div').hide(); $('#module-ports-div').hide();
$('#module-plug-div').hide(); $('#module-plug-div').hide();
$('#module-status-div').hide();
} else if (parseInt($(this).val()) === 0) { } else if (parseInt($(this).val()) === 0) {
$('#module-name-div').show(); $('#module-name-div').show();
$('#module-name').removeAttr('disabled'); $('#module-name').removeAttr('disabled');
@@ -224,6 +229,7 @@ $(document).ready(function () {
$('#module-device-id').attr('disabled', 'disabled'); $('#module-device-id').attr('disabled', 'disabled');
$('#module-ports-div').show(); $('#module-ports-div').show();
$('#module-plug-div').show(); $('#module-plug-div').show();
$('#module-status-div').show();
} else { } else {
$('#module-name-div').show(); $('#module-name-div').show();
$('#module-name').removeAttr('disabled'); $('#module-name').removeAttr('disabled');
@@ -231,6 +237,7 @@ $(document).ready(function () {
$('#module-device-id').attr('disabled', 'disabled'); $('#module-device-id').attr('disabled', 'disabled');
$('#module-ports-div').hide(); $('#module-ports-div').hide();
$('#module-plug-div').hide(); $('#module-plug-div').hide();
$('#module-status-div').hide();
} }
}); });
@@ -280,6 +287,7 @@ $(document).ready(function () {
ports: $.trim($('#module-ports').val()), ports: $.trim($('#module-ports').val()),
plug: $.trim($('#module-plug').val()), plug: $.trim($('#module-plug').val()),
width: $.trim($('#module-width').val()), width: $.trim($('#module-width').val()),
status: $.trim($('#module-status').val()),
position: $.trim($('#module-position').val()) position: $.trim($('#module-position').val())
}, function (data) { }, function (data) {
@@ -327,7 +335,8 @@ $(document).ready(function () {
type: $.trim($('#module-type').val()), type: $.trim($('#module-type').val()),
name: $.trim($('#module-name').val()), name: $.trim($('#module-name').val()),
ports: $.trim($('#module-ports').val()), ports: $.trim($('#module-ports').val()),
plug: $.trim($('#module-plug').val()) plug: $.trim($('#module-plug').val()),
status: $.trim($('#module-status').val())
}, function (data) { }, function (data) {
if (data.success === true) { if (data.success === true) {
$('#rackModuleModal').modal('toggle'); $('#rackModuleModal').modal('toggle');
@@ -509,7 +518,6 @@ $(document).ready(function () {
} }
$('#module-type').attr('disabled', 'disabled'); $('#module-type').attr('disabled', 'disabled');
$('#module-name').val(parent.find('td').eq(tdnumber).data('name')); $('#module-name').val(parent.find('td').eq(tdnumber).data('name'));
// $('#module-name').attr('disabled', 'disabled');
$('#module-remove').data('moduleid', parent.find('td').eq(tdnumber).data('id')); $('#module-remove').data('moduleid', parent.find('td').eq(tdnumber).data('id'));
@@ -568,5 +576,335 @@ $(document).ready(function () {
}); });
}); });
}); });
let currentCableTrigger;
$("body").on("click", ".cable-container", 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');
$('#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);
$('#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();
});
$(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;
}
});
$("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 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;
}
// Wenn die Gruppe für diesen Namen noch nicht existiert, erstelle sie
if (!cableGroups[cableName]) {
cableGroups[cableName] = {
totalFibers: 0
};
}
// 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)`;
}
// Die "id" ist jetzt der eindeutige Kabelname
cableSearchData.push({
id: name,
text: label
});
}
// 3. Select2 mit den gruppierten Daten initialisieren
$('#cable-search').select2({
data: cableSearchData,
placeholder: "Kabelname suchen...",
allowClear: true,
});
}
$('#cable-search').on('select2:select', function (e) {
const selectedData = e.params.data;
// Die "id" aus der Auswahl ist jetzt der Kabelname
const selectedCableName = selectedData.id;
if (!selectedCableName) return;
// 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');
});
});
$('#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();
}); });