added oaid/fcp filter
This commit is contained in:
@@ -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">×</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>
|
||||
|
||||
Reference in New Issue
Block a user