Pop Erweiterung auf Adressen

This commit is contained in:
Daniel Spitzer
2026-02-20 11:22:21 +01:00
parent 86bc518663
commit 4d518981fa
7 changed files with 205 additions and 13 deletions

View File

@@ -70,6 +70,15 @@ if (!empty(trim($pops->vlan_ipv6)))
<th>Kategorie</th>
<td><?= $categoryArray[$pops->category]['name']." (".$categoryArray[$pops->category]['comment'].")" ?> </td>
</tr>
<tr>
<th>Adresse</th>
<td>
<a title="Google-Maps: <?= $pops->street ?> <?= $pops->number ?>, <?= $pops->zip ?> <?= $pops->city ?>"
class="mapsLink"
href="http://maps.google.com/?q=<?= urlencode($pops->street . " " . $pops->number . ", " . $pops->zip . " " . $pops->city) ?>"
target="_blank"><?= $pops->street ?> <?= $pops->number ?>, <?= $pops->zip ?> <?= $pops->city ?></a>
</td>
</tr>
<tr>
<th>Standort</th>
<td>

View File

@@ -112,11 +112,60 @@ if (isset($_GET['returnto']) && $_GET['returnto'] == "pop-detail") {
<div class="form-group row">
<label class="col-lg-2 col-form-label" for="location">Standortinfo</label>
<div class="col-lg-10">
<textarea id="note" class="form-control" name="location"
<textarea id="location" class="form-control" name="location"
rows="5"><?= $pop->location ?></textarea>
</div>
</div>
<div class="form-group row">
<label class="col-lg-2 col-form-label" for="zip">Adresse</label>
<div class="col-lg-1">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">PLZ</span>
</div>
<input type="text" class="form-control" name="zip" id="zip"
value="<?= $pop->zip ?>">
</div>
</div>
<div class="col-lg-4">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Ort</span>
</div>
<input type="text" class="form-control" name="city" id="city"
value="<?= $pop->city ?>">
</div>
</div>
</div>
<div class="form-group row">
<!-- <label class="col-lg-2 col-form-label" for="street">Straße</label>-->
<!-- <div class="col-lg-3">-->
<!-- <input type="text" class="form-control" name="street" id="street"-->
<!-- value="--><?php //= $pop->street ?><!--">-->
<!-- </div>-->
<label class="col-lg-2 col-form-label" for=""></label>
<div class="col-lg-4">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Straße</span>
</div>
<input type="text" class="form-control" name="street" id="street"
value="<?= $pop->street ?>">
</div>
</div>
<div class="col-lg-1">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Nr.</span>
</div>
<input type="text" class="form-control" name="number" id="number"
value="<?= $pop->number ?>">
</div>
</div>
</div>
</div>
</div>

View File

@@ -52,6 +52,10 @@ class PopController extends mfBaseController
"category" => $pop->category ?: 99,
"networkArea" => $pop->networks,
"location" => $pop->location,
"zip" => $pop->zip,
"city" => $pop->city,
"street" => $pop->street,
"number" => $pop->number,
"state" => $pop->state,
"folder_link" => $pop->folder_link,
"doku_date" => $pop->doku_date,
@@ -142,6 +146,7 @@ class PopController extends mfBaseController
{
$network_id = 90;
$this->layout()->set("network_id", $network_id);
$this->layout()->set("networks", NetworkModel::getAll());
$this->layout()->set("categories", $this->getMapCategories());
$this->layout()->setTemplate("Pop/Map");
}
@@ -1295,6 +1300,10 @@ class PopController extends mfBaseController
$data['gps_lat'] = ($r->gps_lat) ? $r->gps_lat : null;
$data['gps_long'] = ($r->gps_long) ? $r->gps_long : null;
$data['location'] = $r->location;
$data['zip'] = $r->zip;
$data['city'] = $r->city;
$data['street'] = $r->street;
$data['number'] = $r->number;
$data['vlan_public'] = ($r->vlan_public) ? $r->vlan_public : null;
$data['vlan_nat'] = ($r->vlan_nat) ? $r->vlan_nat : null;
$data['vlan_ipv6'] = ($r->vlan_ipv6) ? $r->vlan_ipv6 : null;
@@ -1561,7 +1570,7 @@ class PopController extends mfBaseController
$db = FronkDB::singleton();
$pops = [];
$popQuery = "
SELECT DISTINCT p.id, p.name, p.gps_lat, p.gps_long
SELECT DISTINCT p.id, p.name, p.gps_lat, p.gps_long, p.zip, p.city, p.street, p.number
FROM Pop p
INNER JOIN PopNetwork pn ON p.id = pn.pop_id
INNER JOIN Network n ON pn.network_id = n.id
@@ -1577,6 +1586,10 @@ class PopController extends mfBaseController
'name' => $popData->name,
'lat' => floatval($popData->gps_lat),
'lng' => floatval($popData->gps_long),
'zip' => $popData->zip,
'city' => $popData->city,
'street' => $popData->street,
'number' => $popData->number,
'type' => 'pop'
];
}
@@ -1769,9 +1782,62 @@ class PopController extends mfBaseController
}
}
$pipes = [];
$pipeQuery = "
SELECT DISTINCT p.id, p.description, p.coordinates, p.type_tmp, p.coat_color, p.layer, p.status, p.state
FROM FiberPlanPipe p
INNER JOIN FiberPlanPipeEndpoint pe ON p.id = pe.fiberPlanPipe_id
LEFT JOIN FiberPlanDispatcher d ON pe.fiberPlanDispatcher_id = d.id
LEFT JOIN FiberPlanAddress a ON pe.fiberPlanAddress_id = a.id
LEFT JOIN PopNetwork pn ON pe.pop_id = pn.pop_id
WHERE
(d.network_id = $network_id) OR
(a.network_id = $network_id) OR
(pn.network_id = $network_id)
";
$pipeRes = $db->query($pipeQuery);
if ($db->num_rows($pipeRes)) {
while ($pipeData = $db->fetch_object($pipeRes)) {
if (!empty($pipeData->coordinates)) {
$coords_array = json_decode($pipeData->coordinates, true);
if (is_array($coords_array) && count($coords_array) > 0) {
$convertedCoords = [];
foreach ($coords_array as $coord) {
if (isset($coord['gps_lat']) && isset($coord['gps_long'])) {
$convertedCoords[] = [
'lat' => floatval($coord['gps_lat']),
'lng' => floatval($coord['gps_long'])
];
} elseif (isset($coord['lat']) && isset($coord['lng'])) {
$convertedCoords[] = [
'lat' => floatval($coord['lat']),
'lng' => floatval($coord['lng'])
];
}
}
if (count($convertedCoords) >= 2) {
$pipes[] = [
'id' => intval($pipeData->id),
'name' => $pipeData->description,
'coordinates' => $convertedCoords,
'type' => $pipeData->type_tmp,
'color' => $pipeData->coat_color,
'layer' => $pipeData->layer,
'status' => $pipeData->status,
'state' => $pipeData->state
];
}
}
}
}
}
return mfBaseController::returnJson(mfResponse::Ok([
'pops' => $pops,
'cables' => $cables,
'pipes' => $pipes,
'distributors' => $distributors,
'splices' => $splices,
'customerConnections' => $customerConnections

View File

@@ -8,6 +8,10 @@ class PopModel
public $gps_lat = null;
public $gps_long = null;
public $location = null;
public $zip = null;
public $city = null;
public $street = null;
public $number = null;
public $vlan_public = null;
public $vlan_nat = null;
public $vlan_ipv6 = null;

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class PopAddAddressFields extends AbstractMigration
{
public function up(): void
{
$table = $this->table('Pop');
if (!$table->hasColumn('zip')) {
$table->addColumn('zip', 'string', ['null' => true, 'limit' => 10, 'after' => 'location']);
}
if (!$table->hasColumn('city')) {
$table->addColumn('city', 'string', ['null' => true, 'limit' => 255, 'after' => 'zip']);
}
if (!$table->hasColumn('street')) {
$table->addColumn('street', 'string', ['null' => true, 'limit' => 255, 'after' => 'city']);
}
if (!$table->hasColumn('number')) {
$table->addColumn('number', 'string', ['null' => true, 'limit' => 20, 'after' => 'street']);
}
$table->update();
}
public function down(): void
{
$table = $this->table('Pop');
$table->removeColumn('zip')
->removeColumn('city')
->removeColumn('street')
->removeColumn('number')
->update();
}
}

View File

@@ -28,7 +28,12 @@ Vue.component('pop-map-modal', {
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1" :class="{ 'text-white': index === selectedIndex }">{{ pop.name }}</h6>
</div>
<small :class="index === selectedIndex ? 'text-white' : 'text-muted'">{{ categories[pop.category || 99] }} | {{ pop.location }}</small>
<small :class="index === selectedIndex ? 'text-white' : 'text-muted'">
<template v-if="pop.street || pop.city">
{{ pop.street }} {{ pop.number }}, {{ pop.zip }} {{ pop.city }} |
</template>
{{ categories[pop.category || 99] }}
</small>
</a>
</div>
</div>
@@ -257,11 +262,15 @@ Vue.component('pop-map-modal', {
let categoryName = this.categories[category] || 'Unbekannt';
let stateText = this.states[pop.state] || pop.state || '-';
let addressHtml = (pop.street || pop.city)
? `<div><strong>Adresse:</strong> ${pop.street || ''} ${pop.number || ''}, ${pop.zip || ''} ${pop.city || ''}</div>`
: '';
const popupContent = `
<div style="min-width: 200px;">
<h6 class="p-0"><i class="fas fa-building"></i> <strong>${pop.name}</strong></h6>
<hr class="my-2">
${addressHtml}
<div><strong>Kategorie:</strong> ${categoryName}</div>
<div><strong>Status:</strong> ${stateText}</div>
<div><strong>Info:</strong> ${pop.location || '-'}</div>
@@ -300,7 +309,10 @@ Vue.component('pop-map-modal', {
this.filteredPops = this.allPops.filter(pop =>
pop.name.toLowerCase().includes(query) ||
(pop.location && pop.location.toLowerCase().includes(query))
(pop.location && pop.location.toLowerCase().includes(query)) ||
(pop.street && pop.street.toLowerCase().includes(query)) ||
(pop.city && pop.city.toLowerCase().includes(query)) ||
(pop.zip && pop.zip.toLowerCase().includes(query))
).slice(0, 10);
this.showSuggestions = true;
@@ -345,10 +357,20 @@ Vue.component('pop-map-modal', {
this.showSuggestions = false;
this.selectedIndex = -1;
let found = this.markers.find(m => m.popData.name.toLowerCase().includes(query));
let found = this.markers.find(m =>
m.popData.name.toLowerCase().includes(query) ||
(m.popData.street && m.popData.street.toLowerCase().includes(query)) ||
(m.popData.city && m.popData.city.toLowerCase().includes(query)) ||
(m.popData.zip && m.popData.zip.toLowerCase().includes(query))
);
if (!found) {
const hiddenPop = this.allPops.find(p => p.name.toLowerCase().includes(query));
const hiddenPop = this.allPops.find(p =>
p.name.toLowerCase().includes(query) ||
(p.street && p.street.toLowerCase().includes(query)) ||
(p.city && p.city.toLowerCase().includes(query)) ||
(p.zip && p.zip.toLowerCase().includes(query))
);
if (hiddenPop) {
const category = hiddenPop.category || 99;
if (!this.visibleCategories[category]) {

View File

@@ -20,6 +20,17 @@ Vue.component('Pop', {
<a target="_blank" :href="window['TT_CONFIG']['BASE_URL'] +'/Pop/Detail?id=' + row.id">{{row.name}}</a>
</template>
<template v-slot:address="{ row }">
<a
v-if="row.street || row.city"
:title="'Google Maps: ' + row.street + ' ' + row.number + ', ' + row.zip + ' ' + row.city"
class="mapsLink"
:href="'http://maps.google.com/?q=' + row.street + ' ' + row.number + ', ' + row.zip + ' ' + row.city"
target="_blank">
{{row.street}} {{row.number}}, {{row.zip}} {{row.city}}
</a>
</template>
<template v-slot:category="{ row }">
{{ {1: 'Outdoor', 2: 'Indoor', 3: 'Sender/Funk', 4: 'Container', 99: 'Unbekannt'}[row.category] || 'Unbekannt' }}
</template>
@@ -64,12 +75,6 @@ Vue.component('Pop', {
defaultPageSize: 25,
headers: [
{text: 'Name', key: 'name', priority: 10},
{text: 'Kategorie', key: 'category', class: 'text-center', priority: 4, filter: 'select', filterOptions: [
{value: '1', text: 'Outdoor (Kasten/Schrank)'},
{value: '2', text: 'Indoor (Keller Gebäude)'},
{value: '3', text: 'Sender/Funk (Sendemast)'},
{value: '4', text: 'Container (Garage, Container)'},
{value: '99', text: 'Unbekannt'}]},
{text: 'Netzgebiet', key: 'networkArea', class: 'text-center',
// TODO: fix autocomplete Filter
// filter: 'autocomplete',
@@ -78,7 +83,8 @@ Vue.component('Pop', {
},
{text: 'Zutritt', key: 'location', class: 'text-center', priority: 1},
{text: 'Standort', key: 'gps', class: 'text-center', priority: 2},
{text: 'Status', key: 'state', class: 'text-center', priority: 3, filter: 'select', filterOptions: [
{text: 'Adresse', key: 'address', class: 'text-center', priority: 3},
{text: 'Status', key: 'state', class: 'text-center', priority: 4, filter: 'select', filterOptions: [
{value: '1', text: 'Planung'},
{value: '2', text: 'Bauphase'},
{value: '3', text: 'Grobdoku'},