Add spliceCompleted functionality and update WorkorderMph components
This commit is contained in:
@@ -57,16 +57,21 @@
|
||||
}
|
||||
|
||||
.wohneinheit-manager .we-bezeichner {
|
||||
width: 25%;
|
||||
width: 20%;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.wohneinheit-manager .we-status {
|
||||
width: 20%;
|
||||
width: 18%;
|
||||
}
|
||||
|
||||
.wohneinheit-manager .we-splice {
|
||||
width: 12%;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.wohneinheit-manager .we-note {
|
||||
width: 40%;
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
.wohneinheit-manager .we-actions {
|
||||
@@ -74,6 +79,26 @@
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.wohneinheit-manager .we-splice .custom-checkbox-item {
|
||||
padding: 4px;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wohneinheit-manager .we-splice .custom-checkbox-item .checkmark {
|
||||
margin-right: 0;
|
||||
margin-bottom: 2px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.wohneinheit-manager .we-splice .custom-checkbox-item .checkbox-label {
|
||||
font-size: 0.7rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
font-size: 0.85rem;
|
||||
margin-top: 4px;
|
||||
|
||||
@@ -27,8 +27,11 @@ Vue.component('wohneinheit-status-manager', {
|
||||
},
|
||||
template: `
|
||||
<div class="card wohneinheit-manager">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<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">
|
||||
@@ -36,35 +39,56 @@ Vue.component('wohneinheit-status-manager', {
|
||||
Keine Wohneinheiten gefunden.
|
||||
</div>
|
||||
<div v-else class="we-table">
|
||||
<div v-for="we in wohneinheiten" :key="we.wohneinheitId" class="we-row">
|
||||
<div class="we-cell we-bezeichner">
|
||||
<strong>{{ we.bezeichner }}</strong>
|
||||
<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">
|
||||
<tt-select v-model="we.status" :options="statusOptions" sm no-form-group
|
||||
:disabled="isAdmin" @input="markAsChanged(we)"/>
|
||||
<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-note">
|
||||
<tt-textarea v-model="we.note" sm rows="1" no-form-group
|
||||
:disabled="isAdmin"
|
||||
@input="markAsChanged(we)"
|
||||
:placeholder="isAdmin ? 'Keine Notiz' : 'Pflichtfeld'"/>
|
||||
</div>
|
||||
<div class="we-cell we-actions">
|
||||
<span v-if="we.changed" class="text-warning small">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
</span>
|
||||
<tt-button v-if="!isAdmin" @click="saveWohneinheit(we)" text="Speichern" sm
|
||||
:loading="we.saving" :disabled="!we.changed"/>
|
||||
<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>
|
||||
@@ -74,8 +98,16 @@ Vue.component('wohneinheit-status-manager', {
|
||||
data: () => ({
|
||||
loading: true,
|
||||
wohneinheiten: [],
|
||||
statusOptions: []
|
||||
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;
|
||||
@@ -86,10 +118,11 @@ Vue.component('wohneinheit-status-manager', {
|
||||
});
|
||||
this.wohneinheiten = data.wohneinheiten.map(we => ({
|
||||
...we,
|
||||
changed: false,
|
||||
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);
|
||||
@@ -97,25 +130,33 @@ Vue.component('wohneinheit-status-manager', {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
markAsChanged(we) {
|
||||
we.changed = true;
|
||||
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) {
|
||||
if (!we.note || !we.note.trim()) {
|
||||
return window.notify('error', 'Bitte eine Notiz eingeben.');
|
||||
}
|
||||
|
||||
we.saving = true;
|
||||
try {
|
||||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphCompany/updateWohneinheit`, {
|
||||
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,
|
||||
note: we.note
|
||||
spliceCompleted: we.spliceCompleted ? 1 : 0,
|
||||
tuer: we.tuer,
|
||||
zusatz: we.zusatz
|
||||
});
|
||||
if (data.success) {
|
||||
window.notify('success', data.message);
|
||||
we.changed = false;
|
||||
// Silent success or small indicator
|
||||
this.$emit('wohneinheit-updated');
|
||||
} else {
|
||||
window.notify('error', data.message || 'Speichern fehlgeschlagen.');
|
||||
@@ -149,51 +190,44 @@ Vue.component('checkbox-documentation', {
|
||||
<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" :class="{ disabled: isAdmin }">
|
||||
<input type="checkbox" v-model="checkboxes.easement" :disabled="isAdmin">
|
||||
<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" :class="{ disabled: isAdmin }">
|
||||
<input type="checkbox" v-model="checkboxes.btb" :disabled="isAdmin">
|
||||
<label class="custom-checkbox-item">
|
||||
<input type="checkbox" v-model="checkboxes.btb" @change="saveCheckboxes">
|
||||
<span class="checkmark"></span>
|
||||
<span class="checkbox-label">BTB</span>
|
||||
<span class="checkbox-label">Betriebstechnische Begehung</span>
|
||||
</label>
|
||||
|
||||
<label class="custom-checkbox-item" :class="{ disabled: isAdmin }">
|
||||
<input type="checkbox" v-model="checkboxes.fttxLocationSupplied" :disabled="isAdmin">
|
||||
<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" :class="{ disabled: isAdmin }">
|
||||
<input type="checkbox" v-model="checkboxes.conduitToHuepLaid" :disabled="isAdmin">
|
||||
<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" :class="{ disabled: isAdmin }">
|
||||
<input type="checkbox" v-model="checkboxes.huepMounted" :disabled="isAdmin">
|
||||
<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" :class="{ disabled: isAdmin }">
|
||||
<input type="checkbox" v-model="checkboxes.dropCableAvailable" :disabled="isAdmin">
|
||||
<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>
|
||||
|
||||
<label class="custom-checkbox-item" :class="{ disabled: isAdmin }">
|
||||
<input type="checkbox" v-model="checkboxes.spliceCompleted" :disabled="isAdmin">
|
||||
<span class="checkmark"></span>
|
||||
<span class="checkbox-label">Spleiß im HÜP/HAK erledigt</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="mt-3 text-right" v-if="!isAdmin">
|
||||
<tt-button @click="saveCheckboxes" text="Speichern"
|
||||
:loading="saving" additional-class="btn-primary btn-sm"/>
|
||||
<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>
|
||||
@@ -208,15 +242,15 @@ Vue.component('checkbox-documentation', {
|
||||
fttxLocationSupplied: false,
|
||||
conduitToHuepLaid: false,
|
||||
huepMounted: false,
|
||||
dropCableAvailable: false,
|
||||
spliceCompleted: false
|
||||
dropCableAvailable: false
|
||||
}
|
||||
}),
|
||||
methods: {
|
||||
async fetchCheckboxes() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphCompany/getWorkorderById`, {
|
||||
const basePath = this.isAdmin ? '/WorkorderMphAdmin' : '/WorkorderMphCompany';
|
||||
const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}${basePath}/getWorkorderById`, {
|
||||
params: { id: this.workorderMphId }
|
||||
});
|
||||
this.checkboxes = {
|
||||
@@ -225,8 +259,7 @@ Vue.component('checkbox-documentation', {
|
||||
fttxLocationSupplied: !!data.fttxLocationSupplied,
|
||||
conduitToHuepLaid: !!data.conduitToHuepLaid,
|
||||
huepMounted: !!data.huepMounted,
|
||||
dropCableAvailable: !!data.dropCableAvailable,
|
||||
spliceCompleted: !!data.spliceCompleted
|
||||
dropCableAvailable: !!data.dropCableAvailable
|
||||
};
|
||||
} catch (e) {
|
||||
window.notify('error', 'Checkboxen konnten nicht geladen werden.');
|
||||
@@ -238,12 +271,13 @@ Vue.component('checkbox-documentation', {
|
||||
async saveCheckboxes() {
|
||||
this.saving = true;
|
||||
try {
|
||||
const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphCompany/updateCheckboxes`, {
|
||||
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) {
|
||||
window.notify('success', data.message);
|
||||
// Silent success
|
||||
this.$emit('checkboxes-updated');
|
||||
} else {
|
||||
window.notify('error', data.message || 'Speichern fehlgeschlagen.');
|
||||
@@ -357,33 +391,21 @@ Vue.component('workorder-mph-details-manager', {
|
||||
<div class="card-body p-3">
|
||||
<h5 class="card-title mb-3">Neues Dokument hochladen</h5>
|
||||
|
||||
<div class="form-group row mb-2">
|
||||
<label class="col-form-label col-sm-4 col-form-label-sm">Dokumententyp*</label>
|
||||
<div class="col-sm-8">
|
||||
<select v-model="uploadData.documentType" class="form-control form-control-sm" required>
|
||||
<option value="">-- Bitte wählen --</option>
|
||||
<option v-for="doc in requiredDocs" :key="doc.key" :value="doc.key">
|
||||
{{ doc.label }}
|
||||
</option>
|
||||
</select>
|
||||
<small v-if="uploadData.documentType" class="form-text text-muted">
|
||||
<i class="fas fa-info-circle"></i> {{ getDocExample(uploadData.documentType) }}
|
||||
</small>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<div class="form-group row mb-2">
|
||||
<label class="col-form-label col-sm-4 col-form-label-sm">Beschreibung</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" v-model="uploadData.description" class="form-control form-control-sm"
|
||||
placeholder="Optional: Zusätzliche Beschreibung"/>
|
||||
</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"
|
||||
<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>
|
||||
@@ -417,6 +439,12 @@ Vue.component('workorder-mph-details-manager', {
|
||||
},
|
||||
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: {
|
||||
@@ -490,7 +518,7 @@ Vue.component('workorder-mph-details-manager', {
|
||||
if (data.success) {
|
||||
window.notify('success', data.message);
|
||||
this.$refs.fileInput.value = '';
|
||||
this.uploadData = { files: [], documentType: 'photo', description: '' };
|
||||
this.uploadData = { files: [], documentType: '', description: '' };
|
||||
await this.fetchData();
|
||||
} else {
|
||||
window.notify('error', data.error || 'Upload fehlgeschlagen.');
|
||||
|
||||
Reference in New Issue
Block a user