587 lines
27 KiB
JavaScript
587 lines
27 KiB
JavaScript
// WorkorderMphBase.js - Shared components for WorkorderMph module
|
||
|
||
// Traffic light component (reused from WorkorderBase)
|
||
Vue.component('traffic-light-mph', {
|
||
props: ['deadline', 'status'],
|
||
computed: {
|
||
lightInfo() {
|
||
const deadlineDate = moment.unix(this.deadline);
|
||
const daysLeft = deadlineDate.diff(moment(), 'days');
|
||
|
||
if (['completed', 'new', 'cancelled'].includes(this.status)) return { color: '#cccccc', title: 'Status irrelevant für Dringlichkeit' };
|
||
if (!deadlineDate.isValid()) return { color: '#cccccc', title: 'Keine Deadline gesetzt' };
|
||
if (deadlineDate.isBefore(moment())) return { color: '#dc3545', title: 'Deadline überschritten' };
|
||
if (daysLeft <= 7) return { color: '#dc3545', title: 'Dringend: Weniger als 1 Woche' };
|
||
if (daysLeft <= 21) return { color: '#ffc107', title: 'Mittel: Weniger als 3 Wochen' };
|
||
return { color: '#28a745', title: 'Im Plan: Mehr als 3 Wochen' };
|
||
}
|
||
},
|
||
template: `<span :style="{ color: lightInfo.color, fontSize: '1.2em' }" class="mr-2" :title="lightInfo.title">●</span>`
|
||
});
|
||
|
||
// Wohneinheit Status Manager Component
|
||
Vue.component('wohneinheit-status-manager', {
|
||
props: {
|
||
workorderMphId: { type: Number, required: true },
|
||
isAdmin: { type: Boolean, default: false }
|
||
},
|
||
template: `
|
||
<div class="card wohneinheit-manager">
|
||
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||
<h5 class="mb-0"><i class="fas fa-building mr-2"></i>Wohneinheiten Status</h5>
|
||
<a v-if="hausnummerId" :href="'/AddressDB/view/?id=' + hausnummerId" target="_blank" class="text-white small">
|
||
<i class="fas fa-external-link-alt mr-1"></i>AddressDB #{{ hausnummerId }}
|
||
</a>
|
||
</div>
|
||
<div v-if="loading" class="text-center p-5"><i class="fas fa-spinner fa-spin fa-2x"></i></div>
|
||
<div v-else class="card-body p-0">
|
||
<div v-if="wohneinheiten.length === 0" class="alert alert-info m-3">
|
||
Keine Wohneinheiten gefunden.
|
||
</div>
|
||
<div v-else class="we-table">
|
||
<div v-for="we in wohneinheiten" :key="we.wohneinheitId" class="we-row" :class="{ 'locked-row': isLocked(we) }">
|
||
<div class="we-cell we-zusatz">
|
||
<div class="form-group mb-1">
|
||
<small class="text-muted">Zusatz</small>
|
||
<input type="text" class="form-control form-control-sm" v-model="we.zusatz"
|
||
@input="debouncedSave(we)" :disabled="isLocked(we)">
|
||
</div>
|
||
<div class="text-muted small">
|
||
<i class="fas fa-id-card mr-1"></i>OAID: {{ we.oaid }}
|
||
</div>
|
||
<div v-if="we.contact" class="contact-info text-muted">
|
||
<i class="fas fa-user mr-1"></i>{{ we.contact }}
|
||
</div>
|
||
<div v-else-if="we.preorderContact" class="contact-info text-info" title="Daten aus Vorbestellung">
|
||
<i class="fas fa-user-check mr-1"></i>{{ we.preorderContact }}
|
||
</div>
|
||
<div v-else class="text-muted small fst-italic">
|
||
<i class="fas fa-user-slash mr-1"></i>Keine Kontaktinfo
|
||
</div>
|
||
|
||
<div v-if="we.preorderUcode" class="mt-1">
|
||
<a :href="'/Preorder/Index?filter[ucode]=' + we.preorderUcode" target="_blank" class="badge badge-info">
|
||
<i class="fas fa-shopping-cart mr-1"></i>Vorbestellung
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="we-cell we-tuer">
|
||
<div class="form-group mb-0">
|
||
<small class="text-muted">Tür</small>
|
||
<input type="text" class="form-control form-control-sm" v-model="we.tuer"
|
||
@input="debouncedSave(we)" :disabled="isLocked(we)">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="we-cell we-status">
|
||
<small class="text-muted d-block mb-1">Status</small>
|
||
<div v-if="isLocked(we)" class="locked-status">
|
||
<i class="fas fa-lock mr-1"></i> {{ getStatusText(we.status) }}
|
||
</div>
|
||
<tt-select v-else v-model="we.status" :options="filteredStatusOptions" sm no-form-group
|
||
@input="saveWohneinheit(we)"/>
|
||
<div v-if="we.saving" class="text-muted small mt-1"><i class="fas fa-sync fa-spin mr-1"></i>Speichert...</div>
|
||
</div>
|
||
<div class="we-cell we-splice text-center">
|
||
<label class="custom-checkbox-item mb-0" title="Spleiß im HÜP/HAK erledigt">
|
||
<input type="checkbox" v-model="we.spliceCompleted" @change="saveWohneinheit(we)" :disabled="isLocked(we)">
|
||
<span class="checkmark small-checkmark"></span>
|
||
<span class="d-block small mt-1">Spleiß</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`,
|
||
data: () => ({
|
||
loading: true,
|
||
wohneinheiten: [],
|
||
statusOptions: [],
|
||
hausnummerId: null,
|
||
debounceTimers: {}
|
||
}),
|
||
computed: {
|
||
filteredStatusOptions() {
|
||
// Filter out status with code 300
|
||
return this.statusOptions.filter(opt => opt.code !== 300);
|
||
}
|
||
},
|
||
methods: {
|
||
async fetchWohneinheiten() {
|
||
this.loading = true;
|
||
try {
|
||
const basePath = this.isAdmin ? '/WorkorderMphAdmin' : '/WorkorderMphCompany';
|
||
const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}${basePath}/getWohneinheiten`, {
|
||
params: { workorderMphId: this.workorderMphId }
|
||
});
|
||
this.wohneinheiten = data.wohneinheiten.map(we => ({
|
||
...we,
|
||
spliceCompleted: !!we.spliceCompleted,
|
||
saving: false
|
||
}));
|
||
this.statusOptions = data.statusOptions || [];
|
||
this.hausnummerId = data.hausnummerId;
|
||
} catch (e) {
|
||
window.notify('error', 'Wohneinheiten konnten nicht geladen werden.');
|
||
console.error(e);
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
isLocked(we) {
|
||
const status = this.statusOptions.find(s => s.value === we.status);
|
||
return status && status.code === 300;
|
||
},
|
||
debouncedSave(we) {
|
||
we.saving = true;
|
||
if (this.debounceTimers[we.wohneinheitId]) {
|
||
clearTimeout(this.debounceTimers[we.wohneinheitId]);
|
||
}
|
||
this.debounceTimers[we.wohneinheitId] = setTimeout(() => {
|
||
this.saveWohneinheit(we);
|
||
}, 1000);
|
||
},
|
||
async saveWohneinheit(we) {
|
||
we.saving = true;
|
||
try {
|
||
const basePath = this.isAdmin ? '/WorkorderMphAdmin' : '/WorkorderMphCompany';
|
||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}${basePath}/updateWohneinheit`, {
|
||
workorderMphId: this.workorderMphId,
|
||
wohneinheitId: we.wohneinheitId,
|
||
status: we.status,
|
||
spliceCompleted: we.spliceCompleted ? 1 : 0,
|
||
tuer: we.tuer,
|
||
zusatz: we.zusatz
|
||
});
|
||
if (data.success) {
|
||
// Silent success or small indicator
|
||
this.$emit('wohneinheit-updated');
|
||
} else {
|
||
window.notify('error', data.message || 'Speichern fehlgeschlagen.');
|
||
}
|
||
} catch (e) {
|
||
window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.');
|
||
} finally {
|
||
we.saving = false;
|
||
}
|
||
},
|
||
getStatusText(statusValue) {
|
||
const option = this.statusOptions.find(opt => opt.value === statusValue);
|
||
return option ? option.text : '';
|
||
}
|
||
},
|
||
async mounted() {
|
||
await this.fetchWohneinheiten();
|
||
}
|
||
});
|
||
|
||
// Checkbox Documentation Component
|
||
Vue.component('checkbox-documentation', {
|
||
props: {
|
||
workorderMphId: { type: Number, required: true },
|
||
isAdmin: { type: Boolean, default: false }
|
||
},
|
||
template: `
|
||
<div class="checkbox-docs card mb-3">
|
||
<div class="card-body p-3">
|
||
<h5 class="card-title mb-3"><i class="fas fa-clipboard-check mr-2"></i>Dokumentation Checkboxen</h5>
|
||
<div v-if="loading" class="text-center p-3"><i class="fas fa-spinner fa-spin"></i></div>
|
||
<div v-else>
|
||
<div class="custom-checkboxes-grid">
|
||
<label class="custom-checkbox-item">
|
||
<input type="checkbox" v-model="checkboxes.easement" @change="saveCheckboxes">
|
||
<span class="checkmark"></span>
|
||
<span class="checkbox-label">Leitungsrecht</span>
|
||
</label>
|
||
|
||
<label class="custom-checkbox-item">
|
||
<input type="checkbox" v-model="checkboxes.btb" @change="saveCheckboxes">
|
||
<span class="checkmark"></span>
|
||
<span class="checkbox-label">Betriebstechnische Begehung</span>
|
||
</label>
|
||
|
||
<label class="custom-checkbox-item">
|
||
<input type="checkbox" v-model="checkboxes.fttxLocationSupplied" @change="saveCheckboxes">
|
||
<span class="checkmark"></span>
|
||
<span class="checkbox-label">FTTx Location mit Leerrohr versorgt</span>
|
||
</label>
|
||
|
||
<label class="custom-checkbox-item">
|
||
<input type="checkbox" v-model="checkboxes.conduitToHuepLaid" @change="saveCheckboxes">
|
||
<span class="checkmark"></span>
|
||
<span class="checkbox-label">Leerrohr bis HÜP/HAK verlegt</span>
|
||
</label>
|
||
|
||
<label class="custom-checkbox-item">
|
||
<input type="checkbox" v-model="checkboxes.huepMounted" @change="saveCheckboxes">
|
||
<span class="checkmark"></span>
|
||
<span class="checkbox-label">HÜP/HAK montiert</span>
|
||
</label>
|
||
|
||
<label class="custom-checkbox-item">
|
||
<input type="checkbox" v-model="checkboxes.dropCableAvailable" @change="saveCheckboxes">
|
||
<span class="checkmark"></span>
|
||
<span class="checkbox-label">Dropkabel vorhanden</span>
|
||
</label>
|
||
</div>
|
||
<div v-if="saving" class="mt-2 text-right text-muted small">
|
||
<i class="fas fa-sync fa-spin mr-1"></i>Speichert...
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`,
|
||
data: () => ({
|
||
loading: true,
|
||
saving: false,
|
||
checkboxes: {
|
||
easement: false,
|
||
btb: false,
|
||
fttxLocationSupplied: false,
|
||
conduitToHuepLaid: false,
|
||
huepMounted: false,
|
||
dropCableAvailable: false
|
||
}
|
||
}),
|
||
methods: {
|
||
async fetchCheckboxes() {
|
||
this.loading = true;
|
||
try {
|
||
const basePath = this.isAdmin ? '/WorkorderMphAdmin' : '/WorkorderMphCompany';
|
||
const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}${basePath}/getWorkorderById`, {
|
||
params: { id: this.workorderMphId }
|
||
});
|
||
this.checkboxes = {
|
||
easement: !!data.easement,
|
||
btb: !!data.btb,
|
||
fttxLocationSupplied: !!data.fttxLocationSupplied,
|
||
conduitToHuepLaid: !!data.conduitToHuepLaid,
|
||
huepMounted: !!data.huepMounted,
|
||
dropCableAvailable: !!data.dropCableAvailable
|
||
};
|
||
} catch (e) {
|
||
window.notify('error', 'Checkboxen konnten nicht geladen werden.');
|
||
console.error(e);
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
async saveCheckboxes() {
|
||
this.saving = true;
|
||
try {
|
||
const basePath = this.isAdmin ? '/WorkorderMphAdmin' : '/WorkorderMphCompany';
|
||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}${basePath}/updateCheckboxes`, {
|
||
workorderMphId: this.workorderMphId,
|
||
...this.checkboxes
|
||
});
|
||
if (data.success) {
|
||
// Silent success
|
||
this.$emit('checkboxes-updated');
|
||
} else {
|
||
window.notify('error', data.message || 'Speichern fehlgeschlagen.');
|
||
}
|
||
} catch (e) {
|
||
window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.');
|
||
} finally {
|
||
this.saving = false;
|
||
}
|
||
}
|
||
},
|
||
async mounted() {
|
||
await this.fetchCheckboxes();
|
||
}
|
||
});
|
||
|
||
// WorkorderMph Details Manager
|
||
Vue.component('workorder-mph-details-manager', {
|
||
props: {
|
||
workorderMphId: { type: String, required: true },
|
||
isAdmin: { type: Boolean, default: false }
|
||
},
|
||
data: () => ({
|
||
loading: true,
|
||
docs: [],
|
||
journals: [],
|
||
newJournalMessage: '',
|
||
addingJournalEntry: false,
|
||
uploading: false,
|
||
completing: false,
|
||
showCompleteModal: false,
|
||
showAcceptModal: false,
|
||
uploadData: { files: [], documentType: '', description: '' },
|
||
wohneinheitenWithNotes: true,
|
||
requiredDocs: [
|
||
{ key: 'huep_photo', label: 'HÜP/HAK Foto', icon: 'fas fa-camera', example: 'Foto der installierten HÜP/HAK' },
|
||
{ key: 'bep_md_photo', label: 'BEP MD Foto', icon: 'fas fa-camera', example: 'Foto der BEP (MD) Installation' },
|
||
{ key: 'ont_photo', label: 'ONT Foto', icon: 'fas fa-camera', example: 'Foto der ONT Installation' },
|
||
{ key: 'cable_routing', label: 'Kabelverlegung', icon: 'fas fa-route', example: 'Fotos der Kabelverlegung im Treppenhaus' },
|
||
{ key: 'fttx_location', label: 'FTTx Location', icon: 'fas fa-map-marker-alt', example: 'Foto/Dokument der FTTx Location' },
|
||
{ key: 'signature', label: 'Unterschrift', icon: 'fas fa-signature', example: 'Unterschriebenes Übergabeprotokoll' },
|
||
{ key: 'other', label: 'Sonstige Dokumentation', icon: 'fas fa-file', example: 'Weitere relevante Dokumente' }
|
||
]
|
||
}),
|
||
template: `
|
||
<div class="p-3 bg-light">
|
||
<div v-if="loading" class="text-center p-5"><i class="fas fa-spinner fa-spin fa-2x"></i></div>
|
||
<div v-else class="row">
|
||
<div class="col-lg-5 mb-3 mb-lg-0">
|
||
<div v-if="!isAdmin" class="card mb-3">
|
||
<div class="card-body">
|
||
<h5 class="card-title">Auftrag abschließen</h5>
|
||
<p class="small text-muted">Dokumentieren Sie alle Wohneinheiten und laden Sie die erforderlichen Dokumente hoch.</p>
|
||
<hr>
|
||
<tt-button text="Auftrag zur Prüfung einreichen" @click="showCompleteModal = true"
|
||
:disabled="!canComplete || isReadOnly" :loading="completing"
|
||
additional-class="btn-success w-100" icon="fas fa-check-double"/>
|
||
<small v-if="!canComplete && !isReadOnly" class="form-text text-muted text-center mt-2">
|
||
Bitte fügen Sie für jede Wohneinheit eine Notiz hinzu und laden Sie Dokumente hoch.
|
||
</small>
|
||
<div v-if="isReadOnly" class="alert alert-secondary text-center mt-2 p-2">
|
||
Auftrag bereits abgeschlossen oder storniert.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="isAdmin" class="card mb-3">
|
||
<div class="card-body p-3">
|
||
<h5 class="card-title mb-3">Prüfung & Freigabe</h5>
|
||
<p class="small text-muted mb-3">Prüfen Sie die hochgeladenen Dokumente:</p>
|
||
|
||
<div class="required-docs-checklist mb-3">
|
||
<div v-for="doc in requiredDocs" :key="doc.key" class="doc-check-item">
|
||
<i :class="[doc.icon, hasDocType(doc.key) ? 'text-success' : 'text-muted']"></i>
|
||
<span :class="{ 'text-success': hasDocType(doc.key) }">{{ doc.label }}</span>
|
||
<i v-if="hasDocType(doc.key)" class="fas fa-check-circle text-success ml-auto"></i>
|
||
<i v-else class="fas fa-circle text-muted ml-auto" style="font-size: 0.8em;"></i>
|
||
</div>
|
||
</div>
|
||
|
||
<small class="text-muted d-block mb-3">
|
||
<i class="fas fa-info-circle"></i> Stellen Sie sicher, dass alle relevanten Dokumente vorhanden sind.
|
||
</small>
|
||
|
||
<tt-button text="Dokumentation akzeptieren" @click="showAcceptModal = true"
|
||
additional-class="btn-success w-100" icon="fas fa-check"/>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card mt-3">
|
||
<div class="card-header"><h5><i class="fas fa-history mr-2"></i>Journal</h5></div>
|
||
<div class="card-body p-0" style="max-height: 250px; overflow-y: auto;">
|
||
<ul v-if="journals.length" class="list-group list-group-flush">
|
||
<li v-for="log in journals" :key="log.id" class="list-group-item small">
|
||
<strong>{{ formatDate(log.create) }} ({{ log.createByName }}):</strong>
|
||
<div class="text-muted" style="white-space: pre-wrap;">{{ log.text }}</div>
|
||
</li>
|
||
</ul>
|
||
<div v-else class="card-body text-muted text-center">Keine Journaleinträge.</div>
|
||
</div>
|
||
<div class="card-footer" v-if="!isReadOnly">
|
||
<tt-textarea v-model="newJournalMessage" placeholder="Nachricht oder Anmerkung..." rows="2"/>
|
||
<tt-button text="Eintrag speichern" @click="addJournalEntry" :loading="addingJournalEntry"
|
||
additional-class="btn-info btn-sm w-100 mt-2" icon="fas fa-paper-plane"/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-lg-7">
|
||
<div class="card mb-3" v-if="!isReadOnly">
|
||
<div class="card-body p-3">
|
||
<h5 class="card-title mb-3">Neues Dokument hochladen</h5>
|
||
|
||
<tt-select label="Dokumententyp*" :options="docTypeOptions" v-model="uploadData.documentType" sm row required/>
|
||
<div v-if="uploadData.documentType" class="row mb-2">
|
||
<div class="col-sm-8 offset-sm-4">
|
||
<small class="form-text text-muted mt-0">
|
||
<i class="fas fa-info-circle"></i> {{ getDocExample(uploadData.documentType) }}
|
||
</small>
|
||
</div>
|
||
</div>
|
||
|
||
<tt-input label="Beschreibung" v-model="uploadData.description" sm row placeholder="Optional: Zusätzliche Beschreibung"/>
|
||
|
||
<div class="form-group row mb-3">
|
||
<label class="col-form-label col-sm-4 col-form-label-sm">Dateien*</label>
|
||
<div class="col-sm-8">
|
||
<input type="file" class="form-control-file form-control-sm p-0"
|
||
@change="handleFileUpload" ref="fileInput" multiple accept="image/*,.pdf"/>
|
||
<small class="form-text text-muted">Erlaubt: Bilder (JPG, PNG) und PDF</small>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="text-right">
|
||
<tt-button text="Hochladen" @click="uploadFiles" :loading="uploading"
|
||
:disabled="!uploadData.documentType || !uploadData.files.length"
|
||
additional-class="btn-primary btn-sm" icon="fas fa-upload"/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<tt-file-gallery :files="docs" :edit-mode="false" :delete-mode="!isReadOnly && !isAdmin"
|
||
@delete-file="deleteDocumentation">
|
||
</tt-file-gallery>
|
||
</div>
|
||
</div>
|
||
|
||
<tt-modal :show.sync="showCompleteModal" title="Auftrag abschließen" @submit="completeWorkorder" :delete="false">
|
||
Möchten Sie diesen Auftrag wirklich abschließen und zur Prüfung einreichen?
|
||
</tt-modal>
|
||
<tt-modal :show.sync="showAcceptModal" title="Dokumentation akzeptieren" @submit="acceptDocumentation" :delete="false">
|
||
Soll die Dokumentation für diesen Arbeitsauftrag wirklich akzeptiert und der Auftrag abgeschlossen werden?
|
||
</tt-modal>
|
||
</div>
|
||
`,
|
||
computed: {
|
||
isReadOnly() {
|
||
return ['completed', 'cancelled'].includes(this.workorder?.status);
|
||
},
|
||
canComplete() {
|
||
return this.wohneinheitenWithNotes && this.docs.length > 0;
|
||
},
|
||
docTypeOptions() {
|
||
return [
|
||
{ value: '', text: '-- Bitte wählen --' },
|
||
...this.requiredDocs.map(doc => ({ value: doc.key, text: doc.label }))
|
||
];
|
||
}
|
||
},
|
||
methods: {
|
||
formatDate(timestamp) {
|
||
return timestamp ? window.moment.unix(timestamp).format('DD.MM.YYYY HH:mm') : '–';
|
||
},
|
||
hasDocType(docType) {
|
||
return this.docs.some(doc => doc.documentType === docType);
|
||
},
|
||
getDocExample(docType) {
|
||
const doc = this.requiredDocs.find(d => d.key === docType);
|
||
return doc ? doc.example : '';
|
||
},
|
||
async fetchData() {
|
||
this.loading = true;
|
||
try {
|
||
const basePath = this.isAdmin ? '/WorkorderMphAdmin' : '/WorkorderMphCompany';
|
||
const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}${basePath}/getDocumentation`, {
|
||
params: { workorderMphId: this.workorderMphId }
|
||
});
|
||
this.docs = data.docs || [];
|
||
this.journals = data.journals || [];
|
||
} catch (e) {
|
||
window.notify('error', 'Details konnten nicht geladen werden.');
|
||
this.docs = [];
|
||
this.journals = [];
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
async addJournalEntry() {
|
||
if (!this.newJournalMessage.trim()) return window.notify('error', 'Bitte eine Nachricht eingeben.');
|
||
|
||
this.addingJournalEntry = true;
|
||
try {
|
||
const basePath = this.isAdmin ? '/WorkorderMphAdmin' : '/WorkorderMphCompany';
|
||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}${basePath}/addJournal`, {
|
||
workorderMphId: this.workorderMphId,
|
||
text: this.newJournalMessage
|
||
});
|
||
if (data.success) {
|
||
window.notify('success', data.message);
|
||
this.journals = data.journals || [];
|
||
this.newJournalMessage = '';
|
||
} else {
|
||
window.notify('error', data.message || 'Eintrag fehlgeschlagen.');
|
||
}
|
||
} catch (e) {
|
||
window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.');
|
||
} finally {
|
||
this.addingJournalEntry = false;
|
||
}
|
||
},
|
||
handleFileUpload(event) {
|
||
this.uploadData.files = event.target.files;
|
||
},
|
||
async uploadFiles() {
|
||
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('workorderMphId', this.workorderMphId);
|
||
formData.append('documentType', this.uploadData.documentType);
|
||
formData.append('description', this.uploadData.description);
|
||
for (const file of this.uploadData.files) {
|
||
formData.append('file', file);
|
||
}
|
||
|
||
try {
|
||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphCompany/uploadDocumentation`, formData);
|
||
if (data.success) {
|
||
window.notify('success', data.message);
|
||
this.$refs.fileInput.value = '';
|
||
this.uploadData = { files: [], documentType: '', description: '' };
|
||
await this.fetchData();
|
||
} else {
|
||
window.notify('error', data.error || 'Upload fehlgeschlagen.');
|
||
}
|
||
} catch (e) {
|
||
window.notify('error', 'Ein Netzwerkfehler ist beim Upload aufgetreten.');
|
||
} finally {
|
||
this.uploading = false;
|
||
}
|
||
},
|
||
async deleteDocumentation(file) {
|
||
try {
|
||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphCompany/deleteDocumentation`, {
|
||
documentationId: file.id
|
||
});
|
||
if (data.success) {
|
||
window.notify('success', data.message);
|
||
await this.fetchData();
|
||
} else {
|
||
window.notify('error', data.message || 'Löschen fehlgeschlagen.');
|
||
}
|
||
} catch (e) {
|
||
window.notify('error', 'Netzwerkfehler beim Löschen.');
|
||
}
|
||
},
|
||
async completeWorkorder() {
|
||
this.completing = true;
|
||
try {
|
||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphCompany/completeWorkorder`, {
|
||
workorderId: this.workorderMphId
|
||
});
|
||
if (data.success) {
|
||
window.notify('success', data.message);
|
||
this.$emit('workorder-completed');
|
||
this.showCompleteModal = false;
|
||
} else {
|
||
window.notify('error', data.message || 'Abschluss fehlgeschlagen.');
|
||
}
|
||
} catch (e) {
|
||
window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.');
|
||
} finally {
|
||
this.completing = false;
|
||
}
|
||
},
|
||
async acceptDocumentation() {
|
||
try {
|
||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphAdmin/acceptDocumentation`, {
|
||
workorderId: this.workorderMphId
|
||
});
|
||
if (data.success) {
|
||
window.notify('success', data.message);
|
||
this.$emit('documentation-accepted');
|
||
this.showAcceptModal = false;
|
||
} else {
|
||
window.notify('error', data.message || 'Akzeptieren fehlgeschlagen.');
|
||
}
|
||
} catch (e) {
|
||
window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.');
|
||
}
|
||
}
|
||
},
|
||
async mounted() {
|
||
await this.fetchData();
|
||
}
|
||
});
|