Files
thetool/public/js/pages/ADBRimoFcp/ADBRimoFcp.js

245 lines
8.9 KiB
JavaScript

Vue.component('csv-import-modal', {
props: {
show: { type: Boolean, required: true },
title: { type: String, required: true },
apiUrl: { type: String, required: true },
showNetworkAreas: { type: Boolean, default: false },
networkAreas: { type: Array, default: () => [] }
},
data() {
return {
selectedFile: null,
loading: false,
errorMessage: null,
fileInputKey: Date.now(),
selectedNetworkArea: null
};
},
methods: {
handleFileChange(event) {
const file = event.target.files[0];
this.resetInputState();
if (!file) return;
const fileNameLower = file.name.toLowerCase();
const allowedTypes = ['text/csv', 'application/vnd.ms-excel'];
if (!fileNameLower.endsWith('.csv') && !allowedTypes.includes(file.type)) {
this.errorMessage = 'Bitte wählen Sie eine gültige CSV-Datei aus (.csv).';
this.resetFileInputVisuals();
return;
}
this.selectedFile = file;
},
readFileAsString() {
return new Promise((resolve, reject) => {
if (!this.selectedFile) {
return reject(new Error("Keine Datei ausgewählt."));
}
const reader = new FileReader();
reader.onload = (e) => resolve(e.target.result);
reader.onerror = () => reject(new Error("Fehler beim Lesen der Datei."));
reader.readAsText(this.selectedFile, 'UTF-8');
});
},
parseCSV(csvText) {
if (!csvText || typeof csvText !== 'string') return [];
const lines = csvText.trim().split(/\r?\n/);
if (lines.length < 2) return [];
const headers = lines[0].split(';').map(h => this.cleanValue(h));
const data = [];
for (let i = 1; i < lines.length; i++) {
const line = lines[i].trim();
if (!line) continue;
const values = line.split(';');
if (values.length !== headers.length) continue;
const entry = {};
headers.forEach((header, j) => {
entry[header] = this.cleanValue(values[j]);
});
data.push(entry);
}
return data;
},
cleanValue(value) {
if (typeof value !== 'string') return value;
let cleaned = value.trim();
if (cleaned.startsWith('"') && cleaned.endsWith('"')) {
cleaned = cleaned.slice(1, -1).replace(/""/g, '"');
}
return cleaned;
},
async submit() {
if (!this.selectedFile) {
this.errorMessage = 'Bitte wählen Sie zuerst eine Datei aus.';
return;
}
if (this.showNetworkAreas && !this.selectedNetworkArea) {
this.errorMessage = 'Bitte wählen Sie einen Netzbereich aus.';
return;
}
this.errorMessage = null;
this.loading = true;
try {
const csvString = await this.readFileAsString();
const parsedData = this.parseCSV(csvString);
const payload = { data: parsedData };
if (this.showNetworkAreas) {
payload.networkAreaId = this.selectedNetworkArea;
}
const response = await axios.post(this.apiUrl, payload);
window.notify(response.data.success ? 'success' : 'warning', response.data.message || 'Import erfolgreich.');
this.$emit('close', true);
} catch (error) {
let backendMsg = error.response?.data?.message || '';
let detailsMsg = '';
if (error.response?.data?.errors) {
detailsMsg = Object.values(error.response.data.errors).flat().join(', ');
}
this.errorMessage = `Importfehler: ${error.message || 'Unbekannter Fehler.'}${backendMsg ? ' Server: ' + backendMsg : ''}${detailsMsg ? ' Details: ' + detailsMsg : ''}`;
window.notify('error', this.errorMessage);
} finally {
this.loading = false;
}
},
closeModal() {
if (!this.loading) {
this.$emit('close', false);
}
},
resetFileInputVisuals() {
this.fileInputKey = Date.now();
},
resetInputState() {
this.selectedFile = null;
this.errorMessage = null;
},
resetAll() {
this.resetInputState();
this.resetFileInputVisuals();
this.selectedNetworkArea = null;
}
},
watch: {
show(newVal) {
if (!newVal) {
this.resetAll();
}
}
},
template: `
<tt-modal
:show="show"
@update:show="closeModal"
:title="title"
@submit="submit"
:save-loading="loading"
save-text="Importieren"
:delete="false"
>
<tt-loader :absolute="false" v-if="loading"/>
<template v-else>
<div v-if="showNetworkAreas" class="form-group" style="margin: 10px 0">
<tt-select
label="Netzbereich auswählen"
v-model="selectedNetworkArea"
:options="networkAreas"
:required="true"
:disabled="loading || !networkAreas || networkAreas.length === 0"
sm
row
/>
<small v-if="!networkAreas || networkAreas.length === 0" class="form-text text-danger">
Keine Netzbereiche verfügbar.
</small>
</div>
<div class="form-group" style="margin: 10px 0">
<label>CSV-Datei auswählen (Trennzeichen: Semikolon ';')</label>
<input
type="file"
class="form-control"
accept=".csv, text/csv, application/vnd.ms-excel"
@change="handleFileChange"
:key="fileInputKey"
:disabled="loading"
/>
<small v-if="selectedFile" class="form-text text-muted">
Ausgewählt: {{ selectedFile.name }}
</small>
</div>
<div v-if="errorMessage" class="alert alert-danger mt-2" role="alert">
{{ errorMessage }}
</div>
</template>
</tt-modal>
`
});
Vue.component('a-d-b-rimo-fcp', {
template: `
<tt-card>
<tt-table-crud ref="table">
<template #table-top-buttons>
<div style="display: flex; gap: 10px;">
<tt-button icon="fas fa-upload" text="FCPs Importieren" additional-class="btn-outline-success" @click="showImportFCPModal = true" />
<tt-button icon="fas fa-upload" text="Locations Importieren" additional-class="btn-outline-success" @click="showImportLocationsModal = true" />
</div>
</template>
</tt-table-crud>
<csv-import-modal
v-if="showImportFCPModal"
:show="showImportFCPModal"
:show-network-areas="true"
:network-areas="networkAreas"
title="FCPs Importieren"
:api-url="fcpApiUrl"
@close="handleModalClose"
/>
<csv-import-modal
v-if="showImportLocationsModal"
:show="showImportLocationsModal"
:show-network-areas="true"
:network-areas="networkAreas"
title="Locations Importieren"
:api-url="locationsApiUrl"
@close="handleModalClose"
/>
</tt-card>
`,
data() {
return {
showImportFCPModal: false,
showImportLocationsModal: false,
networkAreas: window.TT_CONFIG?.CRUD_CONFIG?.columns?.find(col => col.key === 'netzgebiet_id')?.modal?.items || [],
fcpApiUrl: window.TT_CONFIG['BASE_PATH'] + '/ADBRimoFcp/ImportFCPs',
locationsApiUrl: window.TT_CONFIG['BASE_PATH'] + '/ADBRimoFcp/ImportLocations'
}
},
methods: {
handleModalClose(importWasSuccessful) {
this.showImportFCPModal = false;
this.showImportLocationsModal = false;
if (importWasSuccessful) this.$refs.table.$refs.table.refreshTable();
}
}
});