-
+
@@ -202,205 +228,161 @@ Vue.component('documentation-manager', {
Benötigte Dokumente
-
-
+
Bitte laden Sie alle benötigten Dokumente hoch, um den Auftrag abzuschließen.
-
- Auftrag zur Prüfung eingereicht.
+
+ Auftrag zur Prüfung eingereicht oder storniert.
-
-
-
+
+
-
Falls ein Problem auftritt, das ein Eingreifen erfordert, melden Sie es hier.
-
+
Falls ein Problem auftritt, das ein Eingreifen erfordert, melden Sie es
+ hier.
+
-
- - Keine Einträge vorhanden.
- -
+
- Keine Einträge
+ vorhanden.
+
+ -
{{ formatDate(log.create) }} ({{ log.createByName }}):
{{ log.text }}
-
-
-
+
Neues Dokument hochladen
-
+
-
+
-
+ :edit-mode="!['completed', 'documented', 'cancelled'].includes(workorder.status)"
+ :delete-mode="!['completed', 'documented', 'cancelled'].includes(workorder.status)"
+ @delete-file="deleteDocumentation" @update-file="updateDocumentation">
-
+
-
-
-
+
+
-
-
+
+
-
- `,
- data() {
- return {
- loadingWorkorder: true,
- loadingConfig: true,
- tenantDocTypes: null,
- workorder: null,
- uploading: false,
- completing: false,
- uploadedFiles: [],
- journals: [],
- newJournalMessage: '',
- addingJournalEntry: false,
- interventionData: null,
- interventionTypes: [
- {value: 'stuck', text: 'Ab X Laufmeter stecken geblieben'},
- {value: 'stuck_fcp', text: 'Vom FCP nach HÜP nach X Laufmetern stecken geblieben'},
- {value: 'stuck_hup', text: 'Vom HÜP nach FCP nach X Laufmetern stecken geblieben'},
- {value: 'no_air', text: 'Keine Luftverbindung'},
- {value: 'other', text: 'Sonstiges'}
- ],
- uploadData: {
- files: [],
- documentType: 'photo_hup_mounted',
- description: ''
- }
- }
- },
+
`,
+ data: () => ({
+ loadingWorkorder: true,
+ loadingConfig: true,
+ tenantDocTypes: null,
+ workorder: null,
+ uploading: false,
+ completing: false,
+ uploadedFiles: [],
+ journals: [],
+ newJournalMessage: '',
+ addingJournalEntry: false,
+ interventionData: null,
+ interventionTypes: [{value: 'stuck', text: 'Ab X Laufmeter stecken geblieben'}, {
+ value: 'stuck_fcp',
+ text: 'Vom FCP nach HÜP nach X Laufmetern stecken geblieben'
+ }, {value: 'stuck_hup', text: 'Vom HÜP nach FCP nach X Laufmetern stecken geblieben'}, {
+ value: 'no_air',
+ text: 'Keine Luftverbindung'
+ }, {value: 'other', text: 'Sonstiges'}],
+ uploadData: {files: [], documentType: 'photo_hup_mounted', description: ''}
+ }),
computed: {
requiredDocTypes() {
- if (this.tenantDocTypes) {
- return this.tenantDocTypes;
- }
- // Fallback for RML default
- return [
- { value: 'photo_hup_mounted', text: 'Foto vom montierten HÜP' },
- { value: 'photo_hup_open', text: 'Foto von dem offenen HÜP' },
- { value: 'photo_splice_cassette_hup', text: 'Foto der Spleißkassette – HÜP' },
- { value: 'photo_splice_cassette_fcp', text: 'Foto der Spleißkassette - FCP' },
- { value: 'photo_hup_closed_stickers', text: 'Foto vom geschlossenen HÜP mit allen Aufklebern' },
- { value: 'photo_fcp_labeled', text: 'Foto vom FCP beschriftet' },
- { value: 'photo_patch_position_osp', text: 'Foto der Patch-Position - OSP-Seite' },
- { value: 'photo_patch_position_anb', text: 'Foto der Patch-Position - ANB-Seite' },
- { value: 'measurement_protocol_otdr', text: 'ODTR – Messung (1310nm & 1550nm)' },
- ];
+ if (this.tenantDocTypes) return this.tenantDocTypes;
+ return [{value: 'photo_hup_mounted', text: 'Foto vom montierten HÜP'}, {
+ value: 'photo_hup_open',
+ text: 'Foto von dem offenen HÜP'
+ }, {
+ value: 'photo_splice_cassette_hup',
+ text: 'Foto der Spleißkassette – HÜP'
+ }, {
+ value: 'photo_splice_cassette_fcp',
+ text: 'Foto der Spleißkassette - FCP'
+ }, {
+ value: 'photo_hup_closed_stickers',
+ text: 'Foto vom geschlossenen HÜP mit allen Aufklebern'
+ }, {value: 'photo_fcp_labeled', text: 'Foto vom FCP beschriftet'}, {
+ value: 'photo_patch_position_osp',
+ text: 'Foto der Patch-Position - OSP-Seite'
+ }, {
+ value: 'photo_patch_position_anb',
+ text: 'Foto der Patch-Position - ANB-Seite'
+ }, {value: 'measurement_protocol_otdr', text: 'ODTR – Messung (1310nm & 1550nm)'}];
},
allDocTypes() {
- return [
- ...this.requiredDocTypes,
- { value: 'other', text: 'Sonstiges Dokument (optional)' }
- ];
+ return [...this.requiredDocTypes, {value: 'other', text: 'Sonstiges Dokument (optional)'}];
},
canComplete() {
return this.requiredDocTypes.every(docType => this.isUploaded(docType.value));
},
filesWithStatus() {
- if (!this.journals || this.journals.length === 0) {
- return this.uploadedFiles;
- }
-
- const correctionJournal = [...this.journals]
- .sort((a, b) => b.create - a.create)
- .find(j => j.statusChange && j.statusChange.includes('correction_requested'));
-
- if (!correctionJournal || !correctionJournal.fileIds) {
- return this.uploadedFiles;
- }
-
+ if (!this.journals?.length) return this.uploadedFiles;
+ const correctionJournal = [...this.journals].sort((a, b) => b.create - a.create).find(j => j.statusChange?.includes('correction_requested'));
+ if (!correctionJournal?.fileIds) return this.uploadedFiles;
try {
const incorrectFileIds = JSON.parse(correctionJournal.fileIds);
if (!Array.isArray(incorrectFileIds)) return this.uploadedFiles;
-
- return this.uploadedFiles.map(file => {
- if (incorrectFileIds.includes(file.id)) {
- return { ...file, class: 'border border-danger' };
- }
- return file;
- });
+ return this.uploadedFiles.map(file => incorrectFileIds.includes(file.id) ? {
+ ...file,
+ class: 'border border-danger'
+ } : file);
} catch (e) {
return this.uploadedFiles;
}
@@ -408,20 +390,15 @@ Vue.component('documentation-manager', {
},
methods: {
formatDate(timestamp) {
- if (!timestamp) return '–';
- return window.moment.unix(timestamp).format('DD.MM.YYYY HH:mm');
+ return timestamp ? window.moment.unix(timestamp).format('DD.MM.YYYY HH:mm') : '–';
},
async loadTenantConfig() {
this.loadingConfig = true;
try {
- const response = await axios.get(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/getTenantConfig`, {
- params: { workorderId: this.workorderId }
- });
- if (response.data.success) {
- this.tenantDocTypes = response.data.documentationTypes;
- }
+ const {data} = await axios.get(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/getTenantConfig`, {params: {workorderId: this.workorderId}});
+ if (data.success) this.tenantDocTypes = data.documentationTypes;
} catch (e) {
- console.error("Could not load tenant documentation config", e);
+ console.error("Konnte Mandantenkonfiguration nicht laden", e);
} finally {
this.loadingConfig = false;
}
@@ -429,9 +406,9 @@ Vue.component('documentation-manager', {
async loadWorkorder() {
this.loadingWorkorder = true;
try {
- const response = await axios.get(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/getWorkorderById`, { params: { id: this.workorderId }});
- this.workorder = response.data;
- } catch(e) {
+ const {data} = await axios.get(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/getWorkorderById`, {params: {id: this.workorderId}});
+ this.workorder = data;
+ } catch (e) {
window.notify('error', 'Arbeitsauftragsdetails konnten nicht geladen werden.');
}
this.loadingWorkorder = false;
@@ -441,10 +418,10 @@ Vue.component('documentation-manager', {
},
async fetchDocs() {
try {
- const response = await axios.get(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/getDocumentation`, { params: { workorderId: this.workorderId }});
- this.uploadedFiles = response.data.docs;
- this.journals = response.data.journals;
- } catch(e) {
+ const {data} = await axios.get(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/getDocumentation`, {params: {workorderId: this.workorderId}});
+ this.uploadedFiles = data.docs;
+ this.journals = data.journals;
+ } catch (e) {
window.notify('error', 'Dokumente konnten nicht geladen werden.');
}
},
@@ -452,95 +429,80 @@ Vue.component('documentation-manager', {
this.uploadData.files = event.target.files;
},
async uploadFiles() {
- if(!this.uploadData.files || this.uploadData.files.length === 0) return window.notify('error', 'Bitte eine oder mehrere Dateien auswählen.');
+ if (!this.uploadData.files?.length) return window.notify('error', 'Bitte eine oder mehrere Dateien auswählen.');
this.uploading = true;
-
const formData = new FormData();
formData.append('workorderId', this.workorder.id);
formData.append('documentType', this.uploadData.documentType);
formData.append('description', this.uploadData.description);
- for (const file of this.uploadData.files) {
- formData.append('files[]', file);
- }
-
+ for (const file of this.uploadData.files) formData.append('files[]', file);
try {
- const response = await axios.post(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/uploadDocumentation`, formData);
- if(response.data.success) {
- window.notify('success', response.data.message);
+ const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/uploadDocumentation`, formData);
+ if (data.success) {
+ window.notify('success', data.message);
this.$refs.fileInput.value = '';
this.uploadData.files = [];
this.uploadData.description = '';
-
- this.uploadedFiles = response.data.docs;
- this.workorder = response.data.workorder;
- } else {
- window.notify('error', response.data.error || 'Upload fehlgeschlagen.');
- }
- } catch(e) {
+ this.uploadedFiles = data.docs;
+ this.workorder = data.workorder;
+ } else window.notify('error', data.error || 'Upload fehlgeschlagen.');
+ } catch (e) {
window.notify('error', 'Ein Netzwerkfehler ist beim Upload aufgetreten.');
}
this.uploading = false;
},
async completeWorkorder() {
- if(!confirm('Möchten Sie diesen Auftrag wirklich abschließen und zur Prüfung einreichen?')) return;
+ if (!confirm('Möchten Sie diesen Auftrag wirklich abschließen und zur Prüfung einreichen?')) return;
this.completing = true;
try {
- const response = await axios.post(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/completeWorkorder`, { workorderId: this.workorder.id });
- if(response.data.success) {
- window.notify('success', response.data.message);
+ const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/completeWorkorder`, {workorderId: this.workorder.id});
+ if (data.success) {
+ window.notify('success', data.message);
this.$emit('workorder-completed');
- } else {
- window.notify('error', response.data.message || 'Ein Fehler ist aufgetreten.');
- }
- } catch(e) {
+ } else window.notify('error', data.message || 'Ein Fehler ist aufgetreten.');
+ } catch (e) {
window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.');
}
this.completing = false;
},
async deleteDocumentation(file) {
try {
- const response = await axios.post(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/deleteDocumentation`, { id: file.id });
- if (response.data.success) {
- window.notify('success', response.data.message);
- this.uploadedFiles = response.data.docs;
- } else {
- window.notify('error', response.data.message || 'Löschen fehlgeschlagen.');
- }
+ const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/deleteDocumentation`, {id: file.id});
+ if (data.success) {
+ window.notify('success', data.message);
+ this.uploadedFiles = data.docs;
+ } else window.notify('error', data.message || 'Löschen fehlgeschlagen.');
} catch (e) {
window.notify('error', 'Netzwerkfehler beim Löschen.');
}
},
async updateDocumentation(file) {
try {
- const payload = { id: file.id, documentType: file.documentType };
- const response = await axios.post(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/updateDocumentation`, payload);
- if (response.data.success) {
- window.notify('success', response.data.message);
- this.uploadedFiles = response.data.docs;
- } else {
- window.notify('error', response.data.message || 'Update fehlgeschlagen.');
- }
+ const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/updateDocumentation`, {
+ id: file.id,
+ documentType: file.documentType
+ });
+ if (data.success) {
+ window.notify('success', data.message);
+ this.uploadedFiles = data.docs;
+ } else window.notify('error', data.message || 'Update fehlgeschlagen.');
} catch (e) {
window.notify('error', 'Netzwerkfehler beim Aktualisieren.');
}
},
async addJournalEntry() {
- if (!this.newJournalMessage.trim()) {
- return window.notify('error', 'Bitte geben Sie eine Nachricht ein.');
- }
+ if (!this.newJournalMessage.trim()) return window.notify('error', 'Bitte geben Sie eine Nachricht ein.');
this.addingJournalEntry = true;
try {
- const response = await axios.post(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/addJournal`, {
+ const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/addJournal`, {
workorderId: this.workorderId,
text: this.newJournalMessage
});
- if (response.data.success) {
- window.notify('success', response.data.message || 'Journal-Eintrag hinzugefügt.');
+ if (data.success) {
+ window.notify('success', data.message || 'Journal-Eintrag hinzugefügt.');
this.newJournalMessage = '';
- this.journals = response.data.journals;
- } else {
- window.notify('error', response.data.message || 'Eintrag konnte nicht gespeichert werden.');
- }
+ this.journals = data.journals;
+ } else window.notify('error', data.message || 'Eintrag konnte nicht gespeichert werden.');
} catch (e) {
window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.');
} finally {
@@ -554,66 +516,45 @@ Vue.component('documentation-manager', {
this.interventionData = {
types: [],
details: {
- stuck: { distance: '' },
- stuck_fcp: { distance: '' },
- stuck_hup: { distance: '' },
- other: { reason: '' }
+ stuck: {distance: ''},
+ stuck_fcp: {distance: ''},
+ stuck_hup: {distance: ''},
+ other: {reason: ''}
}
};
},
async requestIntervention() {
- const { types, details } = this.interventionData;
- if (types.length === 0) {
- return window.notify('error', 'Bitte wählen Sie mindestens ein Problem aus.');
- }
-
+ const {types, details} = this.interventionData;
+ if (types.length === 0) return window.notify('error', 'Bitte wählen Sie mindestens ein Problem aus.');
let journalParts = [];
types.sort();
-
for (const type of types) {
let text = '';
- const problemOption = this.interventionTypes.find(o => o.value === type);
- const problemText = problemOption ? problemOption.text : 'Unbekanntes Problem';
-
+ const problemText = this.interventionTypes.find(o => o.value === type)?.text || 'Unbekanntes Problem';
if (['stuck', 'stuck_fcp', 'stuck_hup'].includes(type)) {
const distance = details[type]?.distance;
- if (!distance || isNaN(distance) || distance <= 0) {
- return window.notify('error', `Bitte eine gültige Distanz für "${problemText}" eingeben.`);
- }
+ if (!distance || isNaN(distance) || distance <= 0) return window.notify('error', `Bitte eine gültige Distanz für "${problemText}" eingeben.`);
text = problemText.replace('X', distance);
- } else if (type === 'no_air') {
- text = problemText;
- } else if (type === 'other') {
+ } else if (type === 'no_air') text = problemText;
+ else if (type === 'other') {
const reason = details.other?.reason;
- if (!reason || !reason.trim()) {
- return window.notify('error', 'Bitte geben Sie einen Grund für "Sonstiges" an.');
- }
+ if (!reason?.trim()) return window.notify('error', 'Bitte geben Sie einen Grund für "Sonstiges" an.');
text = `Sonstiges: ${reason.trim()}`;
}
-
- if (text) {
- journalParts.push(text);
- }
+ if (text) journalParts.push(text);
}
-
const journalText = journalParts.join('\\n');
- if (!journalText) {
- return window.notify('error', 'Keine gültigen Problemdetails zum Senden gefunden.');
- }
-
-
+ if (!journalText) return window.notify('error', 'Keine gültigen Problemdetails zum Senden gefunden.');
try {
- const response = await axios.post(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/requestIntervention`, {
+ const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderCompany/requestIntervention`, {
workorderId: this.workorderId,
- journalText: journalText
+ journalText
});
- if (response.data.success) {
- window.notify('success', response.data.message);
+ if (data.success) {
+ window.notify('success', data.message);
this.interventionData = null;
- this.$emit('workorder-completed'); // This just refreshes the table
- } else {
- window.notify('error', response.data.message || 'Ein Fehler ist aufgetreten.');
- }
+ this.$emit('workorder-completed');
+ } else window.notify('error', data.message || 'Ein Fehler ist aufgetreten.');
} catch (e) {
window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.');
}