improved some bugs
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
|
||||
export default {
|
||||
name: 'WorkorderModule',
|
||||
emits: ['navigate', 'toast'],
|
||||
emits: ['navigate', 'toast', 'detail-open', 'detail-close'],
|
||||
props: {
|
||||
user: Object,
|
||||
submodule: String
|
||||
@@ -63,10 +63,10 @@ export default {
|
||||
const problemType = ref('');
|
||||
const problemComment = ref('');
|
||||
|
||||
// Swipe state for list cards
|
||||
const swipeStartX = ref(0);
|
||||
const swipeCardId = ref(null);
|
||||
const swipeOffset = ref({}); // { [workorderId]: offsetX }
|
||||
const swipeOffset = ref({});
|
||||
const swipeTriggered = ref(false);
|
||||
|
||||
// =====================
|
||||
// COMPUTED
|
||||
@@ -136,12 +136,10 @@ export default {
|
||||
const allRequiredComplete = requiredItems.every(c => c.completed);
|
||||
if (!allRequiredComplete) return false;
|
||||
} else if (checklist.value.length > 0) {
|
||||
// If no items are marked as required, check if at least some items are completed
|
||||
const hasAnyCompleted = checklist.value.some(c => c.completed);
|
||||
if (!hasAnyCompleted) return false;
|
||||
const allCompleted = checklist.value.every(c => c.completed);
|
||||
if (!allCompleted) return false;
|
||||
}
|
||||
|
||||
// Check cable data if required
|
||||
if (tenantConfig.value?.requireCableLength && !cableDataForm.value.cableLength?.trim()) {
|
||||
return false;
|
||||
}
|
||||
@@ -201,6 +199,7 @@ export default {
|
||||
selectedWorkorder.value = workorder;
|
||||
isDetailLoading.value = true;
|
||||
expandedCards.value = { customer: true, checklist: true, documentation: false, notes: false, journal: false, cableData: false };
|
||||
emit('detail-open', workorder.id);
|
||||
|
||||
try {
|
||||
// Fetch all workorder details in a single request
|
||||
@@ -233,6 +232,7 @@ export default {
|
||||
tenantConfig.value = null;
|
||||
checklist.value = [];
|
||||
isEditingNotes.value = false;
|
||||
emit('detail-close');
|
||||
};
|
||||
|
||||
const toggleCard = (cardId) => {
|
||||
@@ -535,10 +535,16 @@ export default {
|
||||
}
|
||||
};
|
||||
|
||||
// Swipe handlers for list cards
|
||||
const scrollIntoViewOnFocus = (e) => {
|
||||
setTimeout(() => {
|
||||
e.target.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const handleTouchStart = (e, wo) => {
|
||||
swipeStartX.value = e.touches[0].clientX;
|
||||
swipeCardId.value = wo.id;
|
||||
swipeTriggered.value = false;
|
||||
};
|
||||
|
||||
const handleTouchMove = (e, wo) => {
|
||||
@@ -546,9 +552,11 @@ export default {
|
||||
const currentX = e.touches[0].clientX;
|
||||
const diff = swipeStartX.value - currentX;
|
||||
|
||||
// Only allow left swipe, max 100px
|
||||
if (diff > 0) {
|
||||
swipeOffset.value = { ...swipeOffset.value, [wo.id]: Math.min(diff, 100) };
|
||||
if (diff > 10) {
|
||||
swipeTriggered.value = true;
|
||||
}
|
||||
} else {
|
||||
swipeOffset.value = { ...swipeOffset.value, [wo.id]: 0 };
|
||||
}
|
||||
@@ -558,18 +566,25 @@ export default {
|
||||
if (swipeCardId.value !== wo.id) return;
|
||||
const offset = swipeOffset.value[wo.id] || 0;
|
||||
|
||||
// If swiped more than 60px, trigger navigation
|
||||
if (offset > 60 && wo.customerAddress) {
|
||||
swipeTriggered.value = true;
|
||||
triggerHaptic('light');
|
||||
const address = encodeURIComponent(wo.customerAddress);
|
||||
window.open(`https://maps.google.com/maps?q=${address}`, '_blank');
|
||||
}
|
||||
|
||||
// Reset with animation
|
||||
swipeOffset.value = { ...swipeOffset.value, [wo.id]: 0 };
|
||||
swipeCardId.value = null;
|
||||
};
|
||||
|
||||
const handleCardClick = (wo) => {
|
||||
if (swipeTriggered.value) {
|
||||
swipeTriggered.value = false;
|
||||
return;
|
||||
}
|
||||
openDetail(wo);
|
||||
};
|
||||
|
||||
const getSwipeStyle = (woId) => {
|
||||
const offset = swipeOffset.value[woId] || 0;
|
||||
return {
|
||||
@@ -666,8 +681,10 @@ export default {
|
||||
handleTouchStart,
|
||||
handleTouchMove,
|
||||
handleTouchEnd,
|
||||
handleCardClick,
|
||||
getSwipeStyle,
|
||||
swipeOffset
|
||||
swipeOffset,
|
||||
scrollIntoViewOnFocus
|
||||
};
|
||||
},
|
||||
|
||||
@@ -767,13 +784,13 @@ export default {
|
||||
|
||||
<!-- Card content (slides on swipe) -->
|
||||
<div
|
||||
@click="openDetail(wo)"
|
||||
@click="handleCardClick(wo)"
|
||||
@touchstart="handleTouchStart($event, wo)"
|
||||
@touchmove="handleTouchMove($event, wo)"
|
||||
@touchend="handleTouchEnd($event, wo)"
|
||||
:style="getSwipeStyle(wo.id)"
|
||||
:style="{ ...getSwipeStyle(wo.id), touchAction: 'pan-y' }"
|
||||
:class="[
|
||||
'relative w-full bg-white dark:bg-slate-800 p-4 rounded-xl shadow-sm text-left cursor-pointer',
|
||||
'relative w-full bg-white dark:bg-slate-800 p-4 rounded-xl shadow-sm text-left cursor-pointer card-contrast',
|
||||
'border-l-4',
|
||||
getStatusBorderColor(wo.status)
|
||||
]"
|
||||
@@ -808,14 +825,7 @@ export default {
|
||||
|
||||
<!-- DETAIL VIEW -->
|
||||
<template v-else>
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between px-4 py-3 bg-white dark:bg-slate-800 border-b border-slate-100 dark:border-slate-700 flex-shrink-0">
|
||||
<button @click="closeDetail" class="flex items-center text-slate-600 dark:text-slate-300">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
Zurück
|
||||
</button>
|
||||
<div class="flex items-center gap-2">
|
||||
<span v-if="selectedWorkorder.oaid" class="font-bold text-primary dark:text-sky-400">{{ selectedWorkorder.oaid }}</span>
|
||||
<span v-else class="font-bold text-primary dark:text-sky-400">#{{ selectedWorkorder.id }}</span>
|
||||
@@ -839,7 +849,7 @@ export default {
|
||||
|
||||
<template v-else>
|
||||
<!-- Customer Card (Expanded by default) -->
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl overflow-hidden">
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl overflow-hidden card-contrast">
|
||||
<button
|
||||
@click="toggleCard('customer')"
|
||||
class="w-full flex items-center justify-between p-4 text-left"
|
||||
@@ -897,7 +907,7 @@ export default {
|
||||
</div>
|
||||
|
||||
<!-- Checklist Card -->
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl overflow-hidden">
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl overflow-hidden card-contrast">
|
||||
<button
|
||||
@click="toggleCard('checklist')"
|
||||
class="w-full flex items-center justify-between p-4 text-left"
|
||||
@@ -963,7 +973,7 @@ export default {
|
||||
</div>
|
||||
|
||||
<!-- Documentation Card -->
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl overflow-hidden">
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl overflow-hidden card-contrast">
|
||||
<button
|
||||
@click="toggleCard('documentation')"
|
||||
class="w-full flex items-center justify-between p-4 text-left"
|
||||
@@ -1020,7 +1030,7 @@ export default {
|
||||
</div>
|
||||
|
||||
<!-- Notes Card -->
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl overflow-hidden">
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl overflow-hidden card-contrast">
|
||||
<button
|
||||
@click="toggleCard('notes')"
|
||||
class="w-full flex items-center justify-between p-4 text-left"
|
||||
@@ -1044,6 +1054,7 @@ export default {
|
||||
rows="4"
|
||||
class="w-full p-3 bg-slate-50 dark:bg-slate-700 rounded-lg text-slate-800 dark:text-white border-0 focus:ring-2 focus:ring-primary"
|
||||
placeholder="Notiz eingeben..."
|
||||
@focus="scrollIntoViewOnFocus"
|
||||
></textarea>
|
||||
<div class="flex justify-end gap-2 mt-2">
|
||||
<button @click="cancelEditNotes" class="px-4 py-2 text-sm text-slate-600 dark:text-slate-300">
|
||||
@@ -1067,7 +1078,7 @@ export default {
|
||||
</div>
|
||||
|
||||
<!-- Journal Card -->
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl overflow-hidden">
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl overflow-hidden card-contrast">
|
||||
<button
|
||||
@click="toggleCard('journal')"
|
||||
class="w-full flex items-center justify-between p-4 text-left"
|
||||
@@ -1096,6 +1107,7 @@ export default {
|
||||
placeholder="Neuer Eintrag..."
|
||||
class="flex-1 px-3 py-2 bg-slate-50 dark:bg-slate-700 rounded-lg text-slate-800 dark:text-white border-0"
|
||||
@keyup.enter="addJournalEntry"
|
||||
@focus="scrollIntoViewOnFocus"
|
||||
>
|
||||
<button @click="addJournalEntry" class="px-4 py-2 bg-primary text-white rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
@@ -1117,7 +1129,7 @@ export default {
|
||||
</div>
|
||||
|
||||
<!-- Cable Data Card (only if required) -->
|
||||
<div v-if="tenantConfig && (tenantConfig.requireCableLength || tenantConfig.requireCableType)" class="bg-white dark:bg-slate-800 rounded-xl overflow-hidden">
|
||||
<div v-if="tenantConfig && (tenantConfig.requireCableLength || tenantConfig.requireCableType)" class="bg-white dark:bg-slate-800 rounded-xl overflow-hidden card-contrast">
|
||||
<button
|
||||
@click="toggleCard('cableData')"
|
||||
class="w-full flex items-center justify-between p-4 text-left"
|
||||
@@ -1162,7 +1174,7 @@ export default {
|
||||
</div>
|
||||
|
||||
<!-- Bottom Action Bar -->
|
||||
<div class="absolute bottom-0 left-0 right-0 p-3 bg-white dark:bg-slate-800 border-t border-slate-100 dark:border-slate-700 flex gap-3">
|
||||
<div class="absolute bottom-0 left-0 right-0 p-3 bg-white dark:bg-slate-800 border-t border-slate-100 dark:border-slate-700 flex gap-3" style="padding-bottom: calc(0.75rem + env(safe-area-inset-bottom, 0px));">
|
||||
<button
|
||||
@click="openProblemSheet"
|
||||
class="flex-1 py-3 bg-amber-500 text-white rounded-xl font-medium flex items-center justify-center active:scale-95 transition"
|
||||
@@ -1230,15 +1242,6 @@ export default {
|
||||
</div>
|
||||
<!-- Upload Buttons -->
|
||||
<div class="space-y-2">
|
||||
<input
|
||||
ref="fileInputRef"
|
||||
type="file"
|
||||
accept="image/*,application/pdf"
|
||||
multiple
|
||||
class="hidden"
|
||||
@change="handleFileSelect"
|
||||
capture="environment"
|
||||
>
|
||||
<button
|
||||
@click="triggerFileInput"
|
||||
:disabled="isUploading"
|
||||
@@ -1300,6 +1303,7 @@ export default {
|
||||
rows="3"
|
||||
placeholder="Weitere Details..."
|
||||
class="w-full px-3 py-2 bg-slate-50 dark:bg-slate-700 rounded-lg text-slate-800 dark:text-white border-0"
|
||||
@focus="scrollIntoViewOnFocus"
|
||||
></textarea>
|
||||
</div>
|
||||
<!-- Submit -->
|
||||
@@ -1368,6 +1372,16 @@ export default {
|
||||
</div>
|
||||
</transition>
|
||||
</teleport>
|
||||
|
||||
<input
|
||||
ref="fileInputRef"
|
||||
type="file"
|
||||
accept="image/*,application/pdf"
|
||||
multiple
|
||||
class="hidden"
|
||||
@change="handleFileSelect"
|
||||
capture="environment"
|
||||
>
|
||||
</div>
|
||||
`
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user