// RMLWorkorderAdmin.js Vue.component('r-m-l-workorder-admin', { template: ` {{ getStatusColumn(row.status).text }} {{ formatDate(row.deadlineDate) }} {{ formatDate(row.appointmentDate) }} `, data() { return { assignModalWorkorderId: null, docsModalWorkorderId: null, crudConfig: { ...window.TT_CONFIG.CRUD_CONFIG, additionalActions: [ { "key": "assign", "title": "Firma zuweisen", "class": "fas fa-user-plus text-primary", "condition": (row) => row.status === 'new', }, { "key": "view_docs", "title": "Dokumentation ansehen", "class": "fas fa-folder-open text-info", "condition": (row) => ['documented', 'completed'].includes(row.status), }, ] } } }, methods: { getStatusColumn(status) { const column = this.crudConfig.columns.find(c => c.key === 'status'); return column.table.filterOptions.find(opt => opt.value === status) || {}; }, formatDate(timestamp) { if (!timestamp) return '–'; return window.moment.unix(timestamp).format('DD.MM.YYYY'); } } }); Vue.component('traffic-light', { props: ['deadline', 'status'], computed: { lightInfo() { if (['completed', 'new'].includes(this.status)) return { color: '#cccccc', title: 'Status irrelevant für Dringlichkeit' }; const now = moment(); const deadlineDate = moment.unix(this.deadline); if (!deadlineDate.isValid()) return { color: '#cccccc', title: 'Keine Deadline gesetzt' }; if (deadlineDate.isBefore(now)) return { color: '#dc3545', title: 'Deadline überschritten' }; const daysLeft = deadlineDate.diff(now, 'days'); 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: `●` }); Vue.component('assign-company-modal', { props: ['workorderId'], template: ` `, data() { return { companies: [], selectedCompanyId: null } }, async mounted() { const response = await axios.get(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderAdmin/getCompanies`); this.companies = response.data; }, methods: { async submit() { if (!this.selectedCompanyId) return window.notify('error', 'Bitte eine Firma auswählen.'); try { const response = await axios.post(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderAdmin/assignWorkorder`, { workorderId: this.workorderId, companyId: this.selectedCompanyId }); if(response.data.success) { window.notify('success', response.data.message); this.$emit('close'); } else { window.notify('error', response.data.message || 'Ein Fehler ist aufgetreten.'); } } catch (e) { window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.'); } } } }); Vue.component('documentation-viewer-modal', { props: ['workorderId'], template: ` Hochgeladene Dokumente Keine Dokumente vorhanden. {{ doc.fileName }} Typ: {{ getDocTypeText(doc.documentType) }} Beschreibung: {{ doc.description || '-' }} Hochgeladen von: {{ doc.userName }} am {{ formatDate(doc.create) }} `, data() { return { loading: false, docs: [] } }, methods: { async fetchDocs() { this.loading = true; const response = await axios.get(`${window.TT_CONFIG.BASE_PATH}/RMLWorkorderAdmin/getDocumentation`, { params: { workorderId: this.workorderId }}); this.docs = response.data; this.loading = false; }, formatDate(timestamp) { return window.moment.unix(timestamp).format('DD.MM.YYYY HH:mm'); }, getDocTypeText(type) { const types = [ { value: 'photo_before', text: 'Foto: Zustand vorher' }, { value: 'photo_during', text: 'Foto: Während der Arbeit' }, { value: 'photo_after', text: 'Foto: Zustand nachher' }, { value: 'measurement_protocol', text: 'Messprotokoll (z.B. OTDR)' }, { value: 'customer_signature', text: 'Unterschriebenes Arbeitsprotokoll' }, ]; return types.find(t => t.value === type)?.text || type; } }, mounted() { this.fetchDocs(); } });