Merge branch 'feat/workorder-notiz-ui-improvements' into 'master'
feat: improve notiz column UI and fix memory exhaustion See merge request fronk/thetool!2104
This commit is contained in:
@@ -201,6 +201,17 @@ class WorkorderModel extends TTCrudBaseModel
|
|||||||
return $result ? $result->fetch_assoc()['count'] : 0;
|
return $result ? $result->fetch_assoc()['count'] : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getPreorderIdsByCampaigns(array $campaignIds): array {
|
||||||
|
if (empty($campaignIds)) return [];
|
||||||
|
$db = self::getDB();
|
||||||
|
$campaignIdList = implode(',', array_map('intval', $campaignIds));
|
||||||
|
$result = $db->query("SELECT id FROM `" . FRONKDB_DBNAME . "`.`Preorder` WHERE preordercampaign_id IN ($campaignIdList)");
|
||||||
|
if (!$result || $result->num_rows === 0) return [];
|
||||||
|
$ids = array_column($result->fetch_all(MYSQLI_ASSOC), 'id');
|
||||||
|
$result->free();
|
||||||
|
return $ids;
|
||||||
|
}
|
||||||
|
|
||||||
public static function getTechnicalData(int $workorderId): ?array {
|
public static function getTechnicalData(int $workorderId): ?array {
|
||||||
$workorder = self::get($workorderId);
|
$workorder = self::get($workorderId);
|
||||||
if (!$workorder || !$workorder->preorderId) return null;
|
if (!$workorder || !$workorder->preorderId) return null;
|
||||||
|
|||||||
@@ -253,12 +253,8 @@ class WorkorderBaseController extends TTCrud
|
|||||||
|
|
||||||
$statusesToCheck = ['new', 'assigned', 'scheduled', 'in_progress', 'correction_requested', 'intervention_required', 'civil_engineering_required', 'civil_engineering_completed', 'problem_solved'];
|
$statusesToCheck = ['new', 'assigned', 'scheduled', 'in_progress', 'correction_requested', 'intervention_required', 'civil_engineering_required', 'civil_engineering_completed', 'problem_solved'];
|
||||||
|
|
||||||
// Get ALL preorders for tenant (including deleted/cancelled) to ensure their workorders get archived
|
$allTenantPreorderIds = WorkorderModel::getPreorderIdsByCampaigns($tenantCampaignIds);
|
||||||
// Note: Not passing 'deleted' filter means all preorders are returned regardless of deleted status
|
if (empty($allTenantPreorderIds)) continue;
|
||||||
$allTenantPreorders = PreorderModel::search(['preordercampaign_id' => $tenantCampaignIds]);
|
|
||||||
if(empty($allTenantPreorders)) continue;
|
|
||||||
|
|
||||||
$allTenantPreorderIds = array_map(fn($p) => $p->id, $allTenantPreorders);
|
|
||||||
|
|
||||||
$workordersToCheck = WorkorderModel::getAll([
|
$workordersToCheck = WorkorderModel::getAll([
|
||||||
'status' => $statusesToCheck,
|
'status' => $statusesToCheck,
|
||||||
|
|||||||
@@ -64,16 +64,19 @@ Vue.component('workorder-admin', {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:additionalinfo="{ row }">
|
<template v-slot:additionalinfo="{ row }">
|
||||||
<div v-if="editingAdditionalInfoId === row.id">
|
<div v-if="editingAdditionalInfoId === row.id" class="wo-notiz-edit">
|
||||||
<tt-textarea v-model="tempAdditionalInfo" @keydown.esc.native="cancelEdit" rows="3" no-form-group sm ref="editTextarea"/>
|
<tt-textarea v-model="tempAdditionalInfo" @keydown.esc.native="cancelEdit" :rows="editTextareaRows" no-form-group sm ref="editTextarea"/>
|
||||||
<div class="mt-2 d-flex justify-content-end">
|
<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="Abbrechen" @click="cancelEdit" sm additional-class="btn-secondary mr-2"/>
|
||||||
<tt-button text="Speichern" @click="updateAdditionalInfo(row)" sm additional-class="btn-success"/>
|
<tt-button text="Speichern" @click="updateAdditionalInfo(row)" sm additional-class="btn-success"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="d-flex align-items-start">
|
<div v-else>
|
||||||
<span style="white-space: pre-wrap; max-width: 250px; display: inline-block;">{{ row.additionalInfo || '-' }}</span>
|
<div :class="['wo-notiz-text', expandedNotes.includes(row.id) ? 'wo-notiz-expanded' : '']">{{ row.additionalInfo || '-' }}</div>
|
||||||
<tt-button icon="fas fa-edit" @click="startAdditionalInfoEdit(row)" additional-class="btn-link btn-sm p-0 ml-2" title="Zusatz-Info bearbeiten"/>
|
<div class="wo-notiz-actions">
|
||||||
|
<button v-if="isNoteLong(row.additionalInfo)" class="wo-notiz-toggle" @click="toggleNoteExpand(row.id)">{{ expandedNotes.includes(row.id) ? '▴ weniger' : '▾ mehr' }}</button>
|
||||||
|
<a class="wo-notiz-edit-btn" @click="startAdditionalInfoEdit(row)" title="Zusatz-Info bearbeiten"><i class="fas fa-edit"></i></a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -122,7 +125,7 @@ Vue.component('workorder-admin', {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
window, workordersToAssign: [], editingWorkorderId: null, editingDeadlineId: null, editingAdditionalInfoId: null,
|
window, workordersToAssign: [], editingWorkorderId: null, editingDeadlineId: null, editingAdditionalInfoId: null,
|
||||||
civilEngineeringData: null, tempAdditionalInfo: '', companiesByTenant: {}, companiesLoading: false, massAssignCompanyId: null,
|
civilEngineeringData: null, tempAdditionalInfo: '', expandedNotes: [], companiesByTenant: {}, companiesLoading: false, massAssignCompanyId: null,
|
||||||
cancelWorkorderModalData: null, problemSolvedModalData: null, massAssignModalData: null,
|
cancelWorkorderModalData: null, problemSolvedModalData: null, massAssignModalData: null,
|
||||||
crudConfig: {
|
crudConfig: {
|
||||||
...window.TT_CONFIG.CRUD_CONFIG, selectable: false, expandable: true,
|
...window.TT_CONFIG.CRUD_CONFIG, selectable: false, expandable: true,
|
||||||
@@ -155,7 +158,11 @@ Vue.component('workorder-admin', {
|
|||||||
const firstWorkorder = rows.find(r => r.id === this.workordersToAssign[0]);
|
const firstWorkorder = rows.find(r => r.id === this.workordersToAssign[0]);
|
||||||
if (!firstWorkorder) return [];
|
if (!firstWorkorder) return [];
|
||||||
return this.companiesByTenant[firstWorkorder.tenantId] || [];
|
return this.companiesByTenant[firstWorkorder.tenantId] || [];
|
||||||
}
|
},
|
||||||
|
editTextareaRows() {
|
||||||
|
const lines = (this.tempAdditionalInfo || '').split('\n').length;
|
||||||
|
return String(Math.max(4, lines + 1));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async chargeWorkorder(row) {
|
async chargeWorkorder(row) {
|
||||||
@@ -273,6 +280,15 @@ Vue.component('workorder-admin', {
|
|||||||
this.problemSolvedModalData = null;
|
this.problemSolvedModalData = null;
|
||||||
} else window.notify('error', data.message || 'Ein Fehler ist aufgetreten.');
|
} else window.notify('error', data.message || 'Ein Fehler ist aufgetreten.');
|
||||||
},
|
},
|
||||||
|
isNoteLong(text) {
|
||||||
|
if (!text) return false;
|
||||||
|
return text.split('\n').length > 4 || text.length > 180;
|
||||||
|
},
|
||||||
|
toggleNoteExpand(rowId) {
|
||||||
|
const idx = this.expandedNotes.indexOf(rowId);
|
||||||
|
if (idx === -1) this.expandedNotes.push(rowId);
|
||||||
|
else this.expandedNotes.splice(idx, 1);
|
||||||
|
},
|
||||||
startAdditionalInfoEdit(row) {
|
startAdditionalInfoEdit(row) {
|
||||||
this.editingAdditionalInfoId = row.id;
|
this.editingAdditionalInfoId = row.id;
|
||||||
this.tempAdditionalInfo = row.additionalInfo || '';
|
this.tempAdditionalInfo = row.additionalInfo || '';
|
||||||
|
|||||||
@@ -42,3 +42,57 @@
|
|||||||
.workorder-button {
|
.workorder-button {
|
||||||
padding: 2px !important;
|
padding: 2px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wo-notiz-text {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
max-width: 250px;
|
||||||
|
max-height: calc(5 * 1.5em);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: max-height 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-notiz-text.wo-notiz-expanded {
|
||||||
|
max-height: 2000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-notiz-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-notiz-toggle {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #007bff;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-notiz-toggle:hover {
|
||||||
|
color: #0056b3;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-notiz-edit-btn {
|
||||||
|
color: #007bff;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-notiz-edit-btn:hover {
|
||||||
|
color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-notiz-edit {
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-notiz-edit textarea {
|
||||||
|
min-height: 80px;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,18 +35,20 @@ Vue.component('workorder-company', {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:additionalinfo="{ row }">
|
<template v-slot:additionalinfo="{ row }">
|
||||||
<div v-if="editingAdditionalInfoId === row.id">
|
<div v-if="editingAdditionalInfoId === row.id" class="wo-notiz-edit">
|
||||||
<tt-textarea v-model="tempAdditionalInfo" @keydown.esc.native="cancelEdit" rows="3" no-form-group sm ref="editTextarea"/>
|
<tt-textarea v-model="tempAdditionalInfo" @keydown.esc.native="cancelEdit" :rows="editTextareaRows" no-form-group sm ref="editTextarea"/>
|
||||||
<div class="mt-2 d-flex justify-content-end">
|
<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="Abbrechen" @click="cancelEdit" sm additional-class="btn-secondary mr-2"/>
|
||||||
<tt-button text="Speichern" @click="updateAdditionalInfo(row)" sm additional-class="btn-success"/>
|
<tt-button text="Speichern" @click="updateAdditionalInfo(row)" sm additional-class="btn-success"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="d-flex align-items-start">
|
<div v-else>
|
||||||
<span style="white-space: pre-wrap; max-width: 250px; display: inline-block;">{{ row.additionalInfo || '-' }}</span>
|
<div :class="['wo-notiz-text', expandedNotes.includes(row.id) ? 'wo-notiz-expanded' : '']">{{ row.additionalInfo || '-' }}</div>
|
||||||
<tt-button v-if="!['completed', 'cancelled', 'documented'].includes(row.status)"
|
<div class="wo-notiz-actions">
|
||||||
icon="fas fa-edit" @click="startAdditionalInfoEdit(row)"
|
<button v-if="isNoteLong(row.additionalInfo)" class="wo-notiz-toggle" @click="toggleNoteExpand(row.id)">{{ expandedNotes.includes(row.id) ? '▴ weniger' : '▾ mehr' }}</button>
|
||||||
additional-class="btn-link btn-sm p-0 ml-2" title="Zusatz-Info bearbeiten"/>
|
<a v-if="!['completed', 'cancelled', 'documented'].includes(row.status)"
|
||||||
|
class="wo-notiz-edit-btn" @click="startAdditionalInfoEdit(row)" title="Zusatz-Info bearbeiten"><i class="fas fa-edit"></i></a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -104,6 +106,7 @@ Vue.component('workorder-company', {
|
|||||||
rescheduleModalData: null,
|
rescheduleModalData: null,
|
||||||
editingAdditionalInfoId: null,
|
editingAdditionalInfoId: null,
|
||||||
tempAdditionalInfo: '',
|
tempAdditionalInfo: '',
|
||||||
|
expandedNotes: [],
|
||||||
crudConfig: {
|
crudConfig: {
|
||||||
...window.TT_CONFIG.CRUD_CONFIG,
|
...window.TT_CONFIG.CRUD_CONFIG,
|
||||||
expandable: true,
|
expandable: true,
|
||||||
@@ -121,6 +124,12 @@ Vue.component('workorder-company', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
editTextareaRows() {
|
||||||
|
const lines = (this.tempAdditionalInfo || '').split('\n').length;
|
||||||
|
return String(Math.max(4, lines + 1));
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getStatusColumn(status) {
|
getStatusColumn(status) {
|
||||||
const column = this.crudConfig.columns.find(c => c.key === 'status');
|
const column = this.crudConfig.columns.find(c => c.key === 'status');
|
||||||
@@ -222,6 +231,15 @@ Vue.component('workorder-company', {
|
|||||||
this.$refs.table.$refs.table.refreshTable();
|
this.$refs.table.$refs.table.refreshTable();
|
||||||
} else window.notify('error', data.message || 'Ein Fehler ist aufgetreten.');
|
} else window.notify('error', data.message || 'Ein Fehler ist aufgetreten.');
|
||||||
},
|
},
|
||||||
|
isNoteLong(text) {
|
||||||
|
if (!text) return false;
|
||||||
|
return text.split('\n').length > 4 || text.length > 180;
|
||||||
|
},
|
||||||
|
toggleNoteExpand(rowId) {
|
||||||
|
const idx = this.expandedNotes.indexOf(rowId);
|
||||||
|
if (idx === -1) this.expandedNotes.push(rowId);
|
||||||
|
else this.expandedNotes.splice(idx, 1);
|
||||||
|
},
|
||||||
startAdditionalInfoEdit(row) {
|
startAdditionalInfoEdit(row) {
|
||||||
this.editingAdditionalInfoId = row.id;
|
this.editingAdditionalInfoId = row.id;
|
||||||
this.tempAdditionalInfo = row.additionalInfo || '';
|
this.tempAdditionalInfo = row.additionalInfo || '';
|
||||||
|
|||||||
Reference in New Issue
Block a user