added oaid/fcp filter

This commit is contained in:
Luca Haid
2025-09-08 08:24:44 +00:00
parent 91aa852e26
commit 22031ffb23

View File

@@ -5,6 +5,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<link rel="shortcut icon" href="<?= self::getResourcePath() ?>assets/images/favicon.ico">
<title>Workorders</title>
<link rel="manifest" href="/js/pages/WorkorderBase/manifest.json">
@@ -91,27 +92,51 @@
const missingTasksPopover = reactive({ show: false, tasks: [] });
const installModal = reactive({ show: false });
const isStandalone = ref(false);
const selectedFcp = ref('all');
const isFcpSelectOpen = ref(false);
const API_BASE_URL = window.TT_CONFIG.BASE_PATH || '/WorkorderCompany';
const api = axios.create({ baseURL: API_BASE_URL });
// --- COMPUTED ---
const fcpOptions = computed(() => {
if (!workorders.value || workorders.value.length === 0) {
return [{ value: 'all', text: 'Alle FCPs' }];
}
const fcps = [...new Set(workorders.value.map(wo => wo.rimo_fcp_name).filter(Boolean))].sort();
const options = fcps.map(fcp => ({ value: fcp, text: fcp }));
return [{ value: 'all', text: 'Alle FCPs' }, ...options];
});
const selectedFcpText = computed(() => {
const selectedOption = fcpOptions.value.find(opt => opt.value === selectedFcp.value);
return selectedOption ? selectedOption.text : 'Alle FCPs';
});
const filteredWorkorders = computed(() => {
let filtered = workorders.value.sort((a,b) => (a.deadlineDate || 0) - (b.deadlineDate || 0));
let filtered = workorders.value;
if (selectedFcp.value !== 'all') {
filtered = filtered.filter(wo => wo.rimo_fcp_name === selectedFcp.value);
}
if (searchTerm.value.length > 2) {
const lowerSearch = searchTerm.value.toLowerCase();
return filtered.filter(wo =>
filtered = filtered.filter(wo =>
wo.id.toString().includes(lowerSearch) ||
(wo.customerName && wo.customerName.toLowerCase().includes(lowerSearch)) ||
(wo.street && wo.street.toLowerCase().includes(lowerSearch)) ||
(wo.city && wo.city.toLowerCase().includes(lowerSearch)) ||
(wo.oaid && wo.oaid.toLowerCase().includes(lowerSearch))
(wo.oaid && wo.oaid.toLowerCase().includes(lowerSearch)) ||
(wo.rimo_fcp_name && wo.rimo_fcp_name.toLowerCase().includes(lowerSearch))
);
}
return filtered;
return filtered.sort((a,b) => (a.deadlineDate || 0) - (b.deadlineDate || 0));
});
const googleMapsLink = computed(() => {
if (!selectedWorkorder.value) return '#';
const { street, hausnummer, plz, city } = selectedWorkorder.value;
@@ -120,7 +145,6 @@
});
const checklist = computed(() => {
// Correctly use documentationTypes for the main checklist
if (!tenantConfig.value?.documentationTypes || !Array.isArray(tenantConfig.value.documentationTypes)) return [];
return tenantConfig.value.documentationTypes.map(reqType => {
const isCompleted = documentation.docs.some(doc => doc.documentType === reqType.value);
@@ -215,6 +239,8 @@
try {
await api.post('/updateAdditionalInfo', { workorderId: selectedWorkorder.value.id, additionalInfo: newInfo });
selectedWorkorder.value.additionalInfo = newInfo;
const woInList = workorders.value.find(w => w.id === selectedWorkorder.value.id);
if(woInList) woInList.additionalInfo = newInfo;
await fetchDetails(selectedWorkorder.value.id); // to refresh journal
} catch(e) { console.error("Failed to save info", e); }
finally { isEditingInfo.value = false; }
@@ -264,12 +290,11 @@
const submitProblem = async () => {
if (problemModal.selectedInterventions.length === 0) return;
let journalParts = [];
// Sort to maintain a consistent order
const sortedInterventions = [...problemModal.selectedInterventions].sort((a, b) => a.value.localeCompare(b.value));
for (const type of sortedInterventions) {
let text = type.text;
if (text.includes('X')) { // Check for 'X' literally, not '[X]'
if (text.includes('X')) {
const detail = problemModal.details[type.value] || '';
if (!detail) {
alert(`Bitte geben Sie Details für "${type.text}" an.`);
@@ -312,6 +337,11 @@
} catch(e) { console.error("Failed to complete workorder", e); }
};
const selectFcp = (fcpValue) => {
selectedFcp.value = fcpValue;
isFcpSelectOpen.value = false;
};
onMounted(() => {
fetchWorkorders();
@@ -322,14 +352,15 @@
isLoading, filteredWorkorders, searchTerm, isDetailsPanelOpen, selectedWorkorder, documentation, tenantConfig,
tempAdditionalInfo, isEditingInfo, newJournalEntry, uploadModal, problemModal, isUploading, isChecklistComplete,
checklist, fullscreenViewer, missingTasksPopover, translatedDocs, filteredJournals, installModal, isStandalone,
selectedFcp, isFcpSelectOpen, fcpOptions, selectedFcpText, fetchWorkorders, fetchDetails,
openDetails, closeDetails, getStatusInfo, formatDate, googleMapsLink, startEditInfo, saveAdditionalInfo,
handleFileSelect, executeUpload, addJournalEntry, submitProblem, handleCompleteClick
handleFileSelect, executeUpload, addJournalEntry, submitProblem, handleCompleteClick, selectFcp
};
},
template: `
<div class="relative h-full w-full">
<transition name="overlay">
<div v-if="isDetailsPanelOpen || installModal.show" @click="closeDetails" class="overlay"></div>
<div v-if="isDetailsPanelOpen || installModal.show || isFcpSelectOpen" @click="closeDetails(); isFcpSelectOpen = false" class="overlay"></div>
</transition>
<div :class="{'panel-open': isDetailsPanelOpen}" class="list-container flex flex-col h-full bg-slate-100">
@@ -338,7 +369,7 @@
<img src="/assets/images/xinon-full.png" alt="Logo" class="h-8 w-auto">
<div class="flex items-center space-x-4">
<button v-if="!isStandalone" @click="installModal.show = true" class="text-sm text-primary font-medium hover:underline">
Wie installiere ich die App?
App installieren
</button>
<button @click="fetchWorkorders" class="p-2 rounded-full hover:bg-slate-100">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-slate-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
@@ -347,18 +378,29 @@
</button>
</div>
</div>
<div class="mt-4"><input type="text" v-model="searchTerm" placeholder="Suche..." class="w-full p-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition"></div>
<div class="mt-4 grid grid-cols-2 gap-2">
<input type="text" v-model="searchTerm" placeholder="Suche..." class="w-full p-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition">
<button @click="isFcpSelectOpen = true" class="w-full p-3 border border-slate-300 rounded-lg bg-white text-left flex justify-between items-center text-sm">
<span class="truncate pr-2">{{ selectedFcpText }}</span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-slate-400 flex-shrink-0" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
</div>
</header>
<main class="flex-grow overflow-y-auto p-2 pb-16">
<div v-if="isLoading" class="text-center p-10"><p class="text-slate-500">Lade Aufträge...</p></div>
<div v-else-if="filteredWorkorders.length === 0" class="text-center p-10"><p class="text-slate-500">Keine Aufträge.</p></div>
<div v-else-if="filteredWorkorders.length === 0" class="text-center p-10"><p class="text-slate-500">Keine Aufträge gefunden.</p></div>
<div v-else class="space-y-3">
<div v-for="wo in filteredWorkorders" :key="wo.id" @click="openDetails(wo)" class="bg-white p-4 rounded-lg shadow-md cursor-pointer transition active:scale-[0.98]">
<div class="flex justify-between items-start">
<div class="flex-grow pr-2 min-w-0">
<p class="font-bold text-slate-800 break-words">{{ wo.customerName || 'N/A' }}</p>
<p class="font-bold text-slate-800 break-words">#{{ wo.id }} | {{ wo.customerName || 'N/A' }}</p>
<p class="text-sm text-slate-500 break-words">{{ wo.street }} {{ wo.hausnummer }}, {{ wo.plz }} {{ wo.city }}</p>
<p class="text-xs text-slate-400 mt-1">ID: {{ wo.id }}</p>
<div class="items-center text-xs text-slate-400 mt-1">
<span class="truncate">FCP: {{ wo.rimo_fcp_name || 'N/A' }}</span><br>
<span class="mr-2">OAID: {{ wo.oaid || 'N/A' }}</span>
</div>
</div>
<div class="flex-shrink-0 ml-2 text-right space-y-1">
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium text-white" :class="getStatusInfo(wo.status).color">{{ getStatusInfo(wo.status).text }}</span>
@@ -388,6 +430,16 @@
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd" /></svg>
<span>{{ selectedWorkorder.street }} {{ selectedWorkorder.hausnummer }}, {{ selectedWorkorder.plz }} {{ selectedWorkorder.city }}</span>
</a>
<div class="border-t pt-3 mt-3 grid grid-cols-2 gap-2 text-sm">
<div>
<p class="text-xs text-slate-500 font-semibold">OAID</p>
<p class="text-slate-800">{{ selectedWorkorder.oaid || 'N/A' }}</p>
</div>
<div>
<p class="text-xs text-slate-500 font-semibold">FCP</p>
<p class="text-slate-800">{{ selectedWorkorder.rimo_fcp_name || 'N/A' }}</p>
</div>
</div>
<div class="border-t pt-3 space-y-2">
<a :href="'mailto:' + selectedWorkorder.email" class="flex items-center text-primary hover:underline"><svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor"><path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" /><path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" /></svg><span>{{ selectedWorkorder.email }}</span></a>
<a :href="'tel:' + selectedWorkorder.phone" class="flex items-center text-primary hover:underline"><svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z" /></svg><span>{{ selectedWorkorder.phone }}</span></a>
@@ -485,6 +537,29 @@
</div>
</transition>
<!-- FCP Select Modal -->
<transition name="fade">
<div v-if="isFcpSelectOpen" class="fixed inset-0 z-30 flex items-end sm:items-center sm:justify-center" @click.self="isFcpSelectOpen = false">
<div class="bg-white rounded-t-lg sm:rounded-lg p-4 w-full max-w-sm flex flex-col max-h-[70vh]">
<div class="flex justify-between items-center mb-4 flex-shrink-0">
<h3 class="font-bold text-lg">FCP auswählen</h3>
<button @click="isFcpSelectOpen = false" class="p-1 rounded-full hover:bg-slate-100 text-2xl leading-none">&times;</button>
</div>
<ul class="flex-grow overflow-y-auto -mr-2 pr-2">
<li v-for="option in fcpOptions" :key="option.value" @click="selectFcp(option.value)"
class="flex justify-between items-center p-3 rounded-md hover:bg-slate-100 cursor-pointer text-sm font-medium"
:class="{'bg-primary/10 text-primary': selectedFcp === option.value}">
<span>{{ option.text }}</span>
<svg v-if="selectedFcp === option.value" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-primary" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg>
</li>
</ul>
</div>
</div>
</transition>
<div v-if="uploadModal.show" class="fixed inset-0 bg-black bg-opacity-50 z-30 flex items-center justify-center p-4">
<div class="bg-white rounded-lg p-6 w-full max-w-sm">
<h3 class="font-bold text-lg mb-4">Dokumenttyp wählen</h3>
@@ -576,4 +651,4 @@
</script>
<script src="/js/pages/WorkorderBase/WorkorderServiceWorker.js"></script>
</body>
</html>
</html>