267 lines
13 KiB
JavaScript
267 lines
13 KiB
JavaScript
// WorkorderMphAdmin.js
|
||
Vue.component('workorder-mph-admin', {
|
||
template: `
|
||
<tt-card>
|
||
<tt-table-crud ref="table" :crud-config="crudConfig">
|
||
<template v-slot:hausnummerinfo="{ row }">
|
||
<span class="small">{{ row.street }} {{ row.hausnummer }}<template v-if="row.stiege">/{{ row.stiege }}</template>, {{ row.plz }} {{ row.city }}</span>
|
||
</template>
|
||
|
||
<template v-slot:status="{ row }">
|
||
<traffic-light-mph :deadline="row.deadlineDate" :status="row.status"/>
|
||
<i :class="getStatusColumn(row.status).icon" :title="getStatusColumn(row.status).text"></i>
|
||
<span class="ml-2">{{ getStatusColumn(row.status).text }}</span>
|
||
</template>
|
||
|
||
<template v-slot:companyname="{ row }">
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<div class="flex-grow-1">
|
||
<div v-if="editingWorkorderId === row.id">
|
||
<div v-if="companiesLoading" class="spinner-border spinner-border-sm"></div>
|
||
<tt-select v-else :options="companies" :value="row.companyId"
|
||
@input="assignCompany(row, $event)" @blur="editingWorkorderId = null"
|
||
@keydown.esc.native="editingWorkorderId = null"
|
||
placeholder="Firma zuweisen..." sm no-form-group/>
|
||
</div>
|
||
<div v-else-if="row.status === 'new'">
|
||
<tt-select :options="companies" :value="row.companyId"
|
||
@input="assignCompany(row, $event)" @focus="loadCompanies"
|
||
placeholder="Firma zuweisen..." sm no-form-group/>
|
||
</div>
|
||
<div v-else><span>{{ row.companyName || 'N/A' }}</span></div>
|
||
</div>
|
||
<div style="display: grid; grid-template-columns: repeat(3, auto); gap: 0px; padding-left: 8px;">
|
||
<tt-button v-if="!['completed', 'new', 'cancelled'].includes(row.status)" icon="fas fa-edit"
|
||
@click="startCompanyEdit(row)" additional-class="btn-link workorder-mph-button"
|
||
title="Zuweisung ändern"/>
|
||
<tt-button v-if="!['completed', 'new', 'cancelled'].includes(row.status)" icon="fas fa-user-slash text-warning"
|
||
@click="unassignWorkorderModalData = row" additional-class="btn-link workorder-mph-button"
|
||
title="Zuweisung aufheben"/>
|
||
<tt-button v-if="!['completed', 'cancelled'].includes(row.status)" icon="fas fa-ban text-danger"
|
||
@click="cancelWorkorderModalData = row" additional-class="btn-link workorder-mph-button"
|
||
title="Auftrag stornieren"/>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<template v-slot:additionalinfo="{ row }">
|
||
<div v-if="editingAdditionalInfoId === row.id">
|
||
<tt-textarea v-model="tempAdditionalInfo" @keydown.esc.native="cancelEdit" rows="3" no-form-group sm ref="editTextarea"/>
|
||
<div class="mt-2 d-flex justify-content-end">
|
||
<tt-button text="Abbrechen" @click="cancelEdit" sm additional-class="btn-secondary mr-2"/>
|
||
<tt-button text="Speichern" @click="updateAdditionalInfo(row)" sm additional-class="btn-success"/>
|
||
</div>
|
||
</div>
|
||
<div v-else class="d-flex align-items-start">
|
||
<span style="white-space: pre-wrap; max-width: 250px; display: inline-block;">{{ row.additionalInfo || '-' }}</span>
|
||
<tt-button icon="fas fa-edit" @click="startAdditionalInfoEdit(row)"
|
||
additional-class="btn-link btn-sm p-0 ml-2" title="Zusatz-Info bearbeiten"/>
|
||
</div>
|
||
</template>
|
||
|
||
<template v-slot:deadlinedate="{ row }">
|
||
<div v-if="editingDeadlineId === row.id">
|
||
<tt-date-picker :value="row.deadlineDate" :date-range="false"
|
||
@input="updateDeadline(row, $event)" @blur="editingDeadlineId = null"
|
||
sm no-form-group/>
|
||
</div>
|
||
<div v-else class="d-flex align-items-center">
|
||
<span>{{ formatDate(row.deadlineDate) }}</span>
|
||
<tt-button icon="fas fa-edit" @click="editingDeadlineId = row.id"
|
||
additional-class="btn-link btn-sm p-0 ml-2" title="Deadline ändern"/>
|
||
</div>
|
||
</template>
|
||
|
||
<template v-slot:appointmentdate="{ row }">{{ formatDate(row.appointmentDate, true) }}</template>
|
||
|
||
<template v-slot:expandedRow="{ row }">
|
||
<workorder-mph-data-provider :workorder-mph-id="row.id" v-slot="{ docs, journals, refresh }">
|
||
<div class="workorder-mph-expanded-wrapper">
|
||
<div class="row g-2">
|
||
<!-- Left Column (1/4): Docs Checkbox, Journal, Review -->
|
||
<div class="col-xl-3 col-lg-4">
|
||
<div class="mph-details-stack">
|
||
<checkbox-documentation :workorder-mph-id="parseInt(row.id)" :is-admin="true" @refresh="refresh"/>
|
||
<workorder-mph-journal :journals="journals" :workorder-mph-id="row.id" :is-admin="true" @refresh="refresh"/>
|
||
<workorder-mph-admin-review :docs="docs" :workorder-mph-id="row.id" @refresh="refresh"/>
|
||
</div>
|
||
</div>
|
||
<!-- Right Column (3/4): Wohneinheiten, Documents -->
|
||
<div class="col-xl-9 col-lg-8">
|
||
<div class="mph-details-stack">
|
||
<wohneinheit-status-manager :workorder-mph-id="parseInt(row.id)" :is-admin="true" @refresh="refresh"/>
|
||
<workorder-mph-documents :docs="docs" :workorder-mph-id="row.id" :is-admin="true" @refresh="refresh"/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</workorder-mph-data-provider>
|
||
</template>
|
||
</tt-table-crud>
|
||
|
||
<tt-modal v-if="cancelWorkorderModalData" :show.sync="cancelWorkorderModalData"
|
||
title="Auftrag stornieren" @submit="cancelWorkorder">
|
||
<p>Soll der Auftrag <strong>#{{ cancelWorkorderModalData.id }}</strong> wirklich storniert werden?</p>
|
||
<tt-textarea label="Grund (optional)" v-model="cancelWorkorderModalData.reason" sm row/>
|
||
</tt-modal>
|
||
|
||
<tt-modal v-if="unassignWorkorderModalData" :show.sync="unassignWorkorderModalData"
|
||
title="Zuweisung aufheben" @submit="unassignWorkorder">
|
||
<p>Soll die Zuweisung für Auftrag <strong>#{{ unassignWorkorderModalData.id }}</strong> aufgehoben werden?</p>
|
||
<p class="text-muted small">Aktuell zugewiesen an: <strong>{{ unassignWorkorderModalData.companyName }}</strong></p>
|
||
<tt-textarea label="Grund (optional)" v-model="unassignWorkorderModalData.reason" sm row/>
|
||
</tt-modal>
|
||
</tt-card>
|
||
`,
|
||
data() {
|
||
return {
|
||
window,
|
||
editingWorkorderId: null,
|
||
editingDeadlineId: null,
|
||
editingAdditionalInfoId: null,
|
||
tempAdditionalInfo: '',
|
||
companies: [],
|
||
companiesLoading: false,
|
||
cancelWorkorderModalData: null,
|
||
unassignWorkorderModalData: null,
|
||
crudConfig: {
|
||
...window.TT_CONFIG.CRUD_CONFIG,
|
||
selectable: false,
|
||
expandable: true,
|
||
customRowClass: (row) => {
|
||
if (['completed', 'new', 'cancelled', 'archived'].includes(row.status)) return 'tt-mph-workorder-irrelevant';
|
||
const deadlineDate = moment.unix(row.deadlineDate);
|
||
if (!deadlineDate.isValid()) return 'tt-mph-workorder-irrelevant';
|
||
const daysLeft = deadlineDate.diff(moment(), 'days');
|
||
if (daysLeft <= 7) return 'tt-mph-workorder-urgent';
|
||
if (daysLeft <= 21) return 'tt-mph-workorder-medium';
|
||
return 'tt-mph-workorder-ontrack';
|
||
}
|
||
}
|
||
}
|
||
},
|
||
methods: {
|
||
getStatusColumn(status) {
|
||
const column = this.crudConfig.columns.find(c => c.key === 'status');
|
||
return column.table.filterOptions.find(opt => opt.value === status) || {};
|
||
},
|
||
formatDate(timestamp, withTime = false) {
|
||
if (!timestamp) return '–';
|
||
return window.moment.unix(timestamp).format(withTime ? 'DD.MM.YYYY HH:mm' : 'DD.MM.YYYY');
|
||
},
|
||
async loadCompanies() {
|
||
if (this.companies.length > 0) return;
|
||
this.companiesLoading = true;
|
||
try {
|
||
const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphAdmin/getCompanies`);
|
||
this.companies = data;
|
||
} catch (e) {
|
||
window.notify('error', 'Firmenliste konnte nicht geladen werden.');
|
||
} finally {
|
||
this.companiesLoading = false;
|
||
}
|
||
},
|
||
async startCompanyEdit(row) {
|
||
await this.loadCompanies();
|
||
this.editingWorkorderId = row.id;
|
||
},
|
||
async assignCompany(workorder, companyId) {
|
||
if (!companyId) {
|
||
this.editingWorkorderId = null;
|
||
return;
|
||
}
|
||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphAdmin/assignWorkorder`, {
|
||
workorderId: workorder.id,
|
||
companyId: companyId
|
||
});
|
||
if (data.success) {
|
||
window.notify('success', data.message);
|
||
this.$refs.table.$refs.table.refreshTable();
|
||
} else {
|
||
window.notify('error', data.message || 'Ein Fehler ist aufgetreten.');
|
||
}
|
||
this.editingWorkorderId = null;
|
||
},
|
||
async updateDeadline(workorder, newDate) {
|
||
if (!newDate) {
|
||
this.editingDeadlineId = null;
|
||
return;
|
||
}
|
||
try {
|
||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphAdmin/updateDeadline`, {
|
||
workorderId: workorder.id,
|
||
deadlineDate: newDate
|
||
});
|
||
if (data.success) {
|
||
window.notify('success', data.message);
|
||
this.$refs.table.$refs.table.refreshTable();
|
||
} else {
|
||
window.notify('error', data.message || 'Ein Fehler ist aufgetreten.');
|
||
}
|
||
} catch (e) {
|
||
window.notify('error', 'Netzwerkfehler.');
|
||
} finally {
|
||
this.editingDeadlineId = null;
|
||
}
|
||
},
|
||
startAdditionalInfoEdit(row) {
|
||
this.editingAdditionalInfoId = row.id;
|
||
this.tempAdditionalInfo = row.additionalInfo || '';
|
||
this.$nextTick(() => this.$refs.editTextarea?.$el.querySelector('textarea').focus());
|
||
},
|
||
cancelEdit() {
|
||
this.editingAdditionalInfoId = null;
|
||
this.tempAdditionalInfo = '';
|
||
},
|
||
async updateAdditionalInfo(row) {
|
||
if (row.additionalInfo === this.tempAdditionalInfo) {
|
||
this.cancelEdit();
|
||
return;
|
||
}
|
||
try {
|
||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphAdmin/updateAdditionalInfo`, {
|
||
workorderMphId: row.id,
|
||
additionalInfo: this.tempAdditionalInfo
|
||
});
|
||
if (data.success) {
|
||
window.notify('success', data.message);
|
||
row.additionalInfo = data.newInfo;
|
||
} else {
|
||
window.notify('error', data.message || 'Update fehlgeschlagen.');
|
||
}
|
||
} catch (e) {
|
||
window.notify('error', 'Netzwerkfehler.');
|
||
} finally {
|
||
this.cancelEdit();
|
||
}
|
||
},
|
||
async cancelWorkorder() {
|
||
const { id, reason } = this.cancelWorkorderModalData;
|
||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphAdmin/cancelWorkorder`, {
|
||
workorderId: id,
|
||
reason: reason
|
||
});
|
||
if (data.success) {
|
||
window.notify('success', data.message);
|
||
this.$refs.table.$refs.table.refreshTable();
|
||
this.cancelWorkorderModalData = null;
|
||
} else {
|
||
window.notify('error', data.message || 'Stornierung fehlgeschlagen.');
|
||
}
|
||
},
|
||
async unassignWorkorder() {
|
||
const { id, reason } = this.unassignWorkorderModalData;
|
||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphAdmin/unassignWorkorder`, {
|
||
workorderId: id,
|
||
reason: reason
|
||
});
|
||
if (data.success) {
|
||
window.notify('success', data.message);
|
||
this.$refs.table.$refs.table.refreshTable();
|
||
this.unassignWorkorderModalData = null;
|
||
} else {
|
||
window.notify('error', data.message || 'Aufheben der Zuweisung fehlgeschlagen.');
|
||
}
|
||
}
|
||
}
|
||
}); |