Files
thetool/public/js/pages/IpNetwork/IpNetwork.js
2025-08-21 10:39:04 +02:00

285 lines
12 KiB
JavaScript

// IpNetwork.js
Vue.component('IpNetwork', {
template: `
<tt-card>
<div class="d-flex align-items-center mb-3">
<div>
<tt-button
v-if="currentNetworkData"
@click="navigateBack"
icon="fas fa-arrow-left"
text="Zurück"
additional-class="btn-secondary mr-2"
/>
<tt-button
@click="openModal(null)"
icon="fas fa-plus"
text="Netzwerk hinzufügen"
additional-class="btn-primary"
/>
</div>
<div style="max-width: 400px;min-width:300px; margin-left: 1rem;">
<tt-autocomplete
v-model="globalSearchTerm"
@input="handleGlobalSearchSelect"
:api-url="apiUrl + '/globalSearch'"
placeholder="Suche nach IP, Name, Beschreibung..."
sm
no-form-group
/>
</div>
</div>
<tt-table
:fetch-url="apiUrl + '/get'"
:config="IpNetworkTableConfig"
@row-click="row => row.cidr < 31 && switchCurrentNetwork(row.id)"
@reset-table="resetFiltersAndSwitch"
small
ssr
ref="table"
>
<template v-slot:expandedRow="{ row }">
<div class="p-2 bg-light">
<strong>Beschreibung:</strong>
<span style="white-space: pre-wrap;" v-if="row.description" v-text="row.description"></span>
<em v-else class="text-muted">Keine Beschreibung vorhanden.</em>
</div>
</template>
<template v-slot:actions="{ row }">
<div class="text-center">
<tt-button
icon="far fa-edit"
title="Bearbeiten"
@click.stop="openModal(row)"
additional-class="btn-link text-primary p-0"
sm
/>
</div>
</template>
</tt-table>
<tt-modal
v-if="modalData"
:show="true"
@update:show="closeModal"
@submit="submitModal"
@delete="deleteNetwork"
:delete="!!modalData.id"
:title="modalData.id ? 'Netzwerk bearbeiten' : 'Netzwerk hinzufügen'"
save-text="Speichern"
delete-text="Löschen"
>
<div class="form-row">
<div class="form-group col-md-8">
<label for="network_address">Netzwerkadresse / CIDR</label>
<div class="input-group input-group-sm">
<input type="text" class="form-control" id="network_address" v-model="modalData.network_address" :disabled="!!modalData.id" required placeholder="z.B. 192.168.1.0">
<div class="input-group-append">
<span class="input-group-text">/</span>
</div>
<input type="number" class="form-control" style="max-width: 80px;" v-model="modalData.cidr" :disabled="!!modalData.id" required placeholder="24">
</div>
</div>
<div class="form-group col-md-4">
<label for="status">Status</label>
<select id="status" class="form-control form-control-sm" v-model="modalData.status" required>
<option v-for="option in statusOptions" :value="option.value">{{ option.text }}</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="name">Name</label>
<input type="text" class="form-control form-control-sm" id="name" v-model="modalData.name" required>
</div>
<div class="form-group col-md-6">
<label for="location">Standort</label>
<input type="text" class="form-control form-control-sm" id="location" v-model="modalData.location">
</div>
</div>
<div class="form-group">
<label for="description">Beschreibung</label>
<textarea class="form-control form-control-sm" id="description" v-model="modalData.description" rows="3"></textarea>
</div>
</tt-modal>
</tt-card>
`,
data() {
return {
window: window,
apiUrl: window.TT_CONFIG.API_BASE_URL,
currentNetworkData: null,
modalData: null,
globalSearchTerm: '',
isNavigating: false,
searchDebounce: null,
statusOptions: [
{value: 'active', text: 'Aktiv'},
{value: 'inactive', text: 'Inaktiv'},
{value: 'reserved', text: 'Reserviert'}
],
IpNetworkTableConfig: {
defaultPageSize: 50,
customRowClass: row => (row.cidr < 31 ? 'tt-pointer' : ''),
expandCondition: row => !!row.description,
headers: [
{text: 'Netzwerkadresse', key: 'network_address_str', sortable: true},
{text: 'Name', key: 'name', sortable: true},
{text: 'Status', key: 'status', filter: 'iconSelect', sortable: true,
filterOptions: [
{value: 'active', text: 'Aktiv', icon: 'fas fa-check text-success'},
{value: 'inactive', text: 'Inaktiv', icon: 'fas fa-times text-danger'},
{value: 'reserved', text: 'Reserviert', icon: 'fas fa-lock text-warning'}
]
},
{text: 'Subnetze', key: 'children', filter: 'numberRange', sortable: true},
{text: 'Standort', key: 'location', sortable: true},
{text: 'Aktionen', key: 'actions', sortable: false, class: 'text-center'},
],
tableHeader: 'IPAM - Stamm',
key: 'IpNetwork'
}
}
},
watch: {
globalSearchTerm(newValue) {
if (this.isNavigating) {
this.isNavigating = false;
return;
}
clearTimeout(this.searchDebounce);
this.searchDebounce = setTimeout(() => {
this.$refs.table.filters.globalSearch = this.globalSearchTerm;
}, 300);
}
},
mounted() {
window.addEventListener('popstate', this.handlePopState);
this.handlePopState(); // Initial load based on URL
},
beforeDestroy() {
window.removeEventListener('popstate', this.handlePopState);
},
methods: {
async handleGlobalSearchSelect(selectedNetworkString) {
if (!selectedNetworkString || typeof selectedNetworkString !== 'string') {
this.globalSearchTerm = selectedNetworkString;
return;
}
this.globalSearchTerm = selectedNetworkString;
try {
const response = await axios.get(`${this.apiUrl}/findNetworkByString`, { params: { network_string: selectedNetworkString } });
if (response.data.success) {
this.switchCurrentNetwork(response.data.navigateToId);
this.isNavigating = true;
this.globalSearchTerm = '';
} else {
window.notify('error', response.data.message || 'Netzwerk konnte nicht gefunden werden.');
}
} catch (e) {
window.notify('error', e.response?.data?.message || 'Fehler bei der Netzwerksuche.');
}
},
handlePopState() {
const params = new URLSearchParams(window.location.search);
const parentId = params.get('parent_network_id') || null;
this.switchCurrentNetwork(parentId, false); // false to not push state
},
openModal(row = null) {
if (row) {
this.modalData = {
id: row.id,
network_address: row.network_address_str.split('/')[0],
cidr: row.cidr,
status: row.status,
name: row.name,
description: row.description,
location: row.location,
};
} else {
this.modalData = {
network_address: '',
cidr: '',
status: 'active',
name: '',
description: '',
location: '',
};
}
},
closeModal() {
this.modalData = null;
},
async switchCurrentNetwork(networkId = null, pushState = true) {
this.$refs.table.filters = { ...this.$refs.table.filters, parent_network_id: networkId || undefined };
if (pushState) {
const url = new URL(window.location);
if (networkId) {
url.searchParams.set('parent_network_id', networkId);
} else {
url.searchParams.delete('parent_network_id');
}
window.history.pushState({path: url.href}, '', url.href);
}
if (networkId) {
try {
const response = await axios.get(`${this.apiUrl}/getById?id=${networkId}`);
this.currentNetworkData = response.data.network;
this.IpNetworkTableConfig.tableHeader = `IPAM - ${this.currentNetworkData.network_address_str} ${this.currentNetworkData.name ? '- ' + this.currentNetworkData.name : ''}`;
} catch (e) {
window.notify('error', 'Details des übergeordneten Netzwerks konnten nicht geladen werden.');
this.currentNetworkData = null;
this.IpNetworkTableConfig.tableHeader = 'IPAM - Stamm';
}
} else {
this.currentNetworkData = null;
this.IpNetworkTableConfig.tableHeader = 'IPAM - Stamm';
}
},
resetFiltersAndSwitch() {
this.globalSearchTerm = '';
this.switchCurrentNetwork(null);
},
navigateBack() {
window.history.back();
},
async submitModal() {
const isUpdate = !!this.modalData.id;
const url = isUpdate ? `${this.apiUrl}/update` : `${this.apiUrl}/create`;
const payload = { ...this.modalData, parent_network_id: this.currentNetworkData?.id || null };
try {
const response = await axios.post(url, payload);
if (response.data.success) {
window.notify('success', response.data.message);
this.closeModal();
this.$refs.table.refreshTable();
} else {
window.notify('error', response.data.message || 'Ein Fehler ist aufgetreten.');
}
} catch (e) {
window.notify('error', e.response?.data?.message || 'Ein Netzwerkfehler ist aufgetreten.');
}
},
async deleteNetwork() {
if (!confirm('Sind Sie sicher, dass Sie dieses Netzwerk und alle untergeordneten Netzwerke löschen möchten?')) return;
try {
const response = await axios.post(`${this.apiUrl}/delete`, { id: this.modalData.id });
if (response.data.success) {
window.notify('success', response.data.message);
this.closeModal();
this.$refs.table.refreshTable();
} else {
window.notify('error', response.data.message || 'Ein Fehler ist aufgetreten.');
}
} catch (e) {
window.notify('error', e.response?.data?.message || 'Ein Netzwerkfehler ist aufgetreten.');
}
}
}
})