349 lines
14 KiB
JavaScript
349 lines
14 KiB
JavaScript
Vue.component('construction-consent-signature-pad', {
|
|
props: {
|
|
ownerId: {type: Number, required: true},
|
|
ownerName: {type: String, required: true}
|
|
},
|
|
data() {
|
|
return {
|
|
window: window,
|
|
signaturePad: null,
|
|
consent: null,
|
|
signatureDate: new Date().toLocaleDateString(),
|
|
notes: '',
|
|
penWidth: 2,
|
|
penColor: '#000000',
|
|
screenOrientation: 'landscape'
|
|
}
|
|
},
|
|
//language=Vue
|
|
template: `
|
|
<tt-modal class="signatureModal" :show="true" :delete="false" :save="false" @update:show="$emit('close')" :title="'Unterschrift für Bauvorhaben'">
|
|
<div class="signature-container">
|
|
<div class="signature-header">
|
|
<h4>Einwilligung für {{ ownerName }}</h4>
|
|
<p>Datum: {{ signatureDate }}</p>
|
|
</div>
|
|
|
|
<div class="signature-area" :class="screenOrientation">
|
|
<canvas id="consent-signature-pad" width="800" height="300"></canvas>
|
|
</div>
|
|
|
|
<div class="notes-area mb-3">
|
|
<tt-input v-model="notes" label="Unterschrieben von...." placeholder="Name oder i.V."></tt-input>
|
|
</div>
|
|
|
|
<div class="signature-actions">
|
|
<button class="btn btn-primary" @click="submit()">Speichern</button>
|
|
<button class="btn btn-outline-secondary" @click="signaturePad.clear()">Zurücksetzen</button>
|
|
<button class="btn btn-outline-danger" @click="$emit('close')">Abbrechen</button>
|
|
</div>
|
|
</div>
|
|
</tt-modal>
|
|
`,
|
|
methods: {
|
|
async submit() {
|
|
try {
|
|
if (this.signaturePad.isEmpty()) {
|
|
this.window.notify('error', 'Bitte eine Unterschrift hinzufügen');
|
|
return;
|
|
}
|
|
|
|
const data = this.signaturePad.toDataURL();
|
|
const response = await axios.post(window.TT_CONFIG['BASE_PATH'] + '/ConstructionConsent/sign', {
|
|
owner_id: this.ownerId,
|
|
signature: data,
|
|
signature_name: this.notes,
|
|
signature_date: this.signatureDate
|
|
});
|
|
|
|
if (response.data.success) {
|
|
this.window.notify('success', response.data.message || 'Unterschrift erfolgreich gespeichert');
|
|
this.$emit('close', true); // Pass true to indicate successful signing
|
|
} else {
|
|
this.window.notify('error',
|
|
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message || 'Ein Fehler ist aufgetreten');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error submitting signature:', error);
|
|
this.window.notify('error', 'Ein Fehler ist beim Speichern der Unterschrift aufgetreten');
|
|
}
|
|
},
|
|
updatePenSettings() {
|
|
this.signaturePad.penColor = this.penColor;
|
|
this.signaturePad.penWidth = this.penWidth;
|
|
},
|
|
toggleOrientation() {
|
|
this.screenOrientation = this.screenOrientation === 'landscape' ? 'portrait' : 'landscape';
|
|
this.$nextTick(() => {
|
|
this.resizeCanvas();
|
|
});
|
|
},
|
|
resizeCanvas() {
|
|
const canvas = document.getElementById('consent-signature-pad');
|
|
const container = canvas.parentElement;
|
|
|
|
if (this.screenOrientation === 'landscape') {
|
|
canvas.width = container.offsetWidth - 20;
|
|
canvas.height = 300;
|
|
} else {
|
|
canvas.width = Math.min(container.offsetWidth - 20, 500);
|
|
canvas.height = 500;
|
|
}
|
|
|
|
// Important: maintain drawing after resize
|
|
const data = this.signaturePad.toData();
|
|
this.signaturePad.clear();
|
|
if (data) {
|
|
this.signaturePad.fromData(data);
|
|
}
|
|
}
|
|
},
|
|
mounted() {
|
|
this.$nextTick(() => {
|
|
const canvas = document.getElementById('consent-signature-pad');
|
|
this.signaturePad = new SignaturePad(canvas, {
|
|
penColor: this.penColor,
|
|
penWidth: this.penWidth,
|
|
velocityFilterWeight: 0.5 // Better for tablet input
|
|
});
|
|
|
|
// Initial canvas sizing
|
|
this.resizeCanvas();
|
|
|
|
// Resize on window change
|
|
window.addEventListener('resize', this.resizeCanvas);
|
|
|
|
// Handle tablet touch events better
|
|
canvas.addEventListener('touchstart', function(event) {
|
|
if (event.cancelable) {
|
|
event.preventDefault();
|
|
}
|
|
}, {passive: false});
|
|
});
|
|
},
|
|
beforeDestroy() {
|
|
window.removeEventListener('resize', this.resizeCanvas);
|
|
}
|
|
});
|
|
|
|
// Main Component for Construction Consent Owner Signing
|
|
Vue.component('construction-consent-sign-tablet', {
|
|
data() {
|
|
return {
|
|
window: window,
|
|
searchName: '',
|
|
searchFilter: {
|
|
consent_status: '',
|
|
owner_result: ''
|
|
},
|
|
results: [],
|
|
loading: false,
|
|
showSignaturePad: false,
|
|
selectedConsent: null,
|
|
filterOptions: {
|
|
consent_status: [
|
|
{text: 'Alle Status', value: ''},
|
|
{text: 'Offen', value: 'open'},
|
|
{text: 'In Bearbeitung', value: 'in_progress'},
|
|
{text: 'Abgeschlossen', value: 'completed'}
|
|
],
|
|
owner_result: [
|
|
{text: 'Alle Ergebnisse', value: ''},
|
|
{text: 'Nicht signiert', value: ''},
|
|
{text: 'Zugestimmt', value: 'accepted'},
|
|
{text: 'Abgelehnt', value: 'rejected'}
|
|
]
|
|
},
|
|
tableConfig: {
|
|
key: 'ConstructionConsentTable',
|
|
tableHeader: 'Bauvorhaben Eigentümer',
|
|
defaultPageSize: 10,
|
|
headers: [
|
|
{text: 'Bauvorhaben', key: 'consent_name', sortable: false, filter: false, class: 'text-nowrap', priority: 9},
|
|
{text: 'Projekt', key: 'project_name', sortable: false,filter: false, class: 'text-nowrap', priority: 7},
|
|
{text: 'Eigentümer', key: 'owner_name', sortable: false,filter: false, class: 'text-nowrap', priority: 6},
|
|
{text: 'Adresse', key: 'owner_address', sortable: false,filter: false, class: '', priority: 5},
|
|
{text: 'Eigentümer Status', key: 'owner_status', sortable: false,filter: false, class: 'text-nowrap', priority: 3},
|
|
{text: 'Eigentümer Ergebnis', key: 'owner_result', sortable: false,filter: false, class: 'text-nowrap', priority: 2},
|
|
{text: 'Aktionen', key: 'actions', sortable: false,filter: false, class: 'text-nowrap text-center', priority: 1}
|
|
],
|
|
}
|
|
}
|
|
},
|
|
//language=Vue
|
|
template: `
|
|
<div class="consent-signing-container">
|
|
<tt-card>
|
|
<div class="search-area p-3">
|
|
<div style="display: grid; grid-template-columns: 4fr 1fr; grid-gap: 16px; justify-content: space-between; align-items: center;">
|
|
<tt-input
|
|
v-model="searchName"
|
|
label="Eigentümer suchen"
|
|
placeholder="Name eingeben..."
|
|
></tt-input>
|
|
|
|
<tt-button
|
|
style="margin-top: 10px"
|
|
@click="searchOwners"
|
|
text="Suchen"
|
|
additionalClass="btn-primary"
|
|
icon="fas fa-search"
|
|
:loading="loading"
|
|
></tt-button>
|
|
</div>
|
|
</div>
|
|
</tt-card>
|
|
|
|
<tt-card v-if="results.length > 0" class="mt-3">
|
|
<tt-table
|
|
:data="results"
|
|
:config="tableConfig"
|
|
excel-export
|
|
>
|
|
<template v-slot:owner_status="{ row }">
|
|
<span class="badge" :class="getOwnerStatusBadgeClass(row.owner_status)">
|
|
{{ getOwnerStatusText(row.owner_status) }}
|
|
</span>
|
|
</template>
|
|
|
|
<template v-slot:owner_result="{ row }">
|
|
<span class="badge" :class="getOwnerResultBadgeClass(row.owner_result)">
|
|
{{ getOwnerResultText(row.owner_result) }}
|
|
</span>
|
|
</template>
|
|
|
|
<template v-slot:actions="{ row }">
|
|
<div class="btn-group btn-group-sm">
|
|
<tt-button
|
|
@click="openSignaturePad(row)"
|
|
text="Unterschreiben"
|
|
:sm="true"
|
|
additionalClass="btn-primary"
|
|
icon="fas fa-signature"
|
|
:disabled="row.owner_result === 'accepted'"
|
|
></tt-button>
|
|
<a :href="window.TT_CONFIG['BASE_PATH'] + '/ConstructionConsent/view/?id=' + row.consent_id"
|
|
class="btn btn-sm btn-info"
|
|
target="_blank"
|
|
title="Ansehen">
|
|
<i class="far fa-eye"></i>
|
|
</a>
|
|
<a :href="window.TT_CONFIG['BASE_PATH'] + '/ConstructionConsent/Download/?open=true&owner_id=' + row.owner_id"
|
|
class="btn btn-sm btn-info"
|
|
target="_blank"
|
|
title="PDF">
|
|
<i class="fas fa-file-pdf"></i>
|
|
</a>
|
|
</div>
|
|
</template>
|
|
</tt-table>
|
|
</tt-card>
|
|
|
|
<tt-card v-else-if="results.length === 0 && !loading" class="mt-3">
|
|
<div class="alert alert-info">
|
|
Keine Ergebnisse gefunden. Bitte versuchen Sie eine andere Suche.
|
|
</div>
|
|
</tt-card>
|
|
|
|
<construction-consent-signature-pad
|
|
v-if="showSignaturePad"
|
|
:owner-id="selectedConsent.owner_id"
|
|
:owner-name="selectedConsent.owner_name"
|
|
@close="handleSignatureClose"
|
|
></construction-consent-signature-pad>
|
|
</div>
|
|
`,
|
|
methods: {
|
|
async searchOwners() {
|
|
this.loading = true;
|
|
try {
|
|
const response = await axios.post(
|
|
window.TT_CONFIG['BASE_PATH'] + '/ConstructionConsent/searchTablet',
|
|
{
|
|
name: this.searchName,
|
|
filter: this.searchFilter
|
|
}
|
|
);
|
|
|
|
if (response.data.success) {
|
|
this.results = response.data.results;
|
|
} else {
|
|
this.window.notify('error', response.data.message || 'Fehler bei der Suche');
|
|
this.results = [];
|
|
}
|
|
} catch (error) {
|
|
console.error('Error searching owners:', error);
|
|
this.window.notify('error', 'Ein Fehler ist bei der Suche aufgetreten');
|
|
this.results = [];
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
openSignaturePad(consent) {
|
|
this.selectedConsent = consent;
|
|
this.showSignaturePad = true;
|
|
},
|
|
handleSignatureClose(success) {
|
|
this.showSignaturePad = false;
|
|
if (success) {
|
|
// Refresh the data after successful signing
|
|
this.searchOwners();
|
|
}
|
|
},
|
|
getStatusBadgeClass(status) {
|
|
switch (status) {
|
|
case 'open': return 'badge-primary';
|
|
case 'in_progress': return 'badge-warning';
|
|
case 'completed': return 'badge-success';
|
|
default: return 'badge-secondary';
|
|
}
|
|
},
|
|
getStatusText(status) {
|
|
switch (status) {
|
|
case 'open': return 'Offen';
|
|
case 'in_progress': return 'In Bearbeitung';
|
|
case 'completed': return 'Abgeschlossen';
|
|
default: return status;
|
|
}
|
|
},
|
|
getOwnerStatusBadgeClass(status) {
|
|
switch (status) {
|
|
case 'pending': return 'badge-warning';
|
|
case 'contacted': return 'badge-info';
|
|
case 'signed': return 'badge-success';
|
|
case 'returned': return 'badge-success';
|
|
default: return 'badge-secondary';
|
|
}
|
|
},
|
|
getOwnerStatusText(status) {
|
|
switch (status) {
|
|
case 'pending': return 'Ausstehend';
|
|
case 'contacted': return 'Kontaktiert';
|
|
case 'signed': return 'Unterschrieben';
|
|
case 'returned': return 'Zurückgegeben';
|
|
default: return status;
|
|
}
|
|
},
|
|
getOwnerResultBadgeClass(result) {
|
|
if (!result) return 'badge-primary';
|
|
switch (result) {
|
|
case 'accepted': return 'badge-success';
|
|
case 'rejected': return 'badge-danger';
|
|
case 'denied': return 'badge-danger';
|
|
default: return 'badge-secondary';
|
|
}
|
|
},
|
|
getOwnerResultText(result) {
|
|
if (!result) return 'Nicht signiert';
|
|
switch (result) {
|
|
case 'accepted': return 'Zugestimmt';
|
|
case 'rejected': return 'Abgelehnt';
|
|
default: return result;
|
|
}
|
|
}
|
|
},
|
|
mounted() {
|
|
// Initialize with empty search to show all results
|
|
this.searchOwners();
|
|
}
|
|
}); |