Views added: - ShiftsView (Schichtplanung) - PatrolsView (Wächterkontrolle) - IncidentsView (Vorfallberichte) - VehiclesView (Fahrzeuge) - DocumentsView (Dokumente) - CustomersView (Kunden/CRM) - BillingView (Abrechnung) - ObjectsView (enhanced with contacts, instructions) Updated: - Router with all new routes - Sidebar with complete navigation
138 lines
5.5 KiB
Vue
138 lines
5.5 KiB
Vue
<script setup lang="ts">
|
||
import { ref, onMounted } from 'vue'
|
||
import { api } from '@/api'
|
||
|
||
const loading = ref(true)
|
||
const documents = ref<any[]>([])
|
||
const categories = ref<any[]>([])
|
||
const pendingDocs = ref<any[]>([])
|
||
const showModal = ref(false)
|
||
const form = ref({ title: '', description: '', category_id: '', file_url: '', is_mandatory: false })
|
||
|
||
onMounted(async () => { await loadData() })
|
||
|
||
async function loadData() {
|
||
loading.value = true
|
||
try {
|
||
const [docRes, catRes, pendRes] = await Promise.all([
|
||
api.get<any>('/documents'),
|
||
api.get<any>('/documents/categories'),
|
||
api.get<any>('/documents/pending/list')
|
||
])
|
||
documents.value = docRes.data.documents || []
|
||
categories.value = catRes.data.categories || []
|
||
pendingDocs.value = pendRes.data.documents || []
|
||
} catch (e) { console.error(e) }
|
||
loading.value = false
|
||
}
|
||
|
||
async function acknowledge(docId: string) {
|
||
try {
|
||
await api.post(`/documents/${docId}/acknowledge`, {})
|
||
await loadData()
|
||
} catch (e: any) { alert('Fehler: ' + e.message) }
|
||
}
|
||
|
||
async function createDocument() {
|
||
try {
|
||
await api.post('/documents', form.value)
|
||
showModal.value = false
|
||
form.value = { title: '', description: '', category_id: '', file_url: '', is_mandatory: false }
|
||
await loadData()
|
||
} catch (e: any) { alert('Fehler: ' + e.message) }
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="p-6">
|
||
<div class="flex justify-between items-center mb-6">
|
||
<div>
|
||
<h1 class="text-2xl font-bold">📁 Dokumente</h1>
|
||
<p class="text-gray-500">Unterlagen & Bestätigungen</p>
|
||
</div>
|
||
<button @click="showModal = true" class="btn btn-primary">+ Dokument</button>
|
||
</div>
|
||
|
||
<!-- Pending Documents Alert -->
|
||
<div v-if="pendingDocs.length > 0" class="bg-yellow-50 border-l-4 border-yellow-400 p-4 mb-6">
|
||
<div class="flex">
|
||
<span class="text-yellow-600 text-xl mr-3">⚠️</span>
|
||
<div>
|
||
<h3 class="font-semibold text-yellow-800">{{ pendingDocs.length }} Dokument(e) zu bestätigen</h3>
|
||
<div class="mt-2 space-y-2">
|
||
<div v-for="doc in pendingDocs" :key="doc.id" class="flex items-center justify-between bg-white p-2 rounded">
|
||
<span>{{ doc.category_icon }} {{ doc.title }}</span>
|
||
<button @click="acknowledge(doc.id)" class="text-sm text-blue-600 hover:underline">Bestätigen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="loading" class="text-center py-12">Laden...</div>
|
||
|
||
<div v-else-if="documents.length === 0" class="bg-gray-50 rounded-lg p-12 text-center text-gray-500">
|
||
<p class="text-4xl mb-4">📄</p>
|
||
<p>Keine Dokumente vorhanden</p>
|
||
</div>
|
||
|
||
<div v-else class="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
<div v-for="doc in documents" :key="doc.id" class="bg-white rounded-lg shadow p-4 hover:shadow-md transition-shadow">
|
||
<div class="flex items-start space-x-3">
|
||
<span class="text-2xl">{{ doc.category_icon || '📄' }}</span>
|
||
<div class="flex-1 min-w-0">
|
||
<h3 class="font-semibold truncate">{{ doc.title }}</h3>
|
||
<p class="text-sm text-gray-500">{{ doc.category_name }}</p>
|
||
<p v-if="doc.description" class="text-sm text-gray-400 mt-1 line-clamp-2">{{ doc.description }}</p>
|
||
</div>
|
||
</div>
|
||
<div class="mt-3 flex items-center justify-between text-xs">
|
||
<span v-if="doc.is_mandatory" class="px-2 py-1 bg-red-100 text-red-700 rounded">Pflicht</span>
|
||
<span v-if="doc.acknowledged" class="text-green-600">✓ Bestätigt</span>
|
||
<span v-else-if="doc.is_mandatory" class="text-orange-600">Ausstehend</span>
|
||
</div>
|
||
<a v-if="doc.file_url" :href="doc.file_url" target="_blank"
|
||
class="mt-3 block text-center text-sm text-blue-600 hover:underline">
|
||
📥 Herunterladen
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal -->
|
||
<div v-if="showModal" class="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
||
<div class="bg-white rounded-lg shadow-xl w-full max-w-md p-6">
|
||
<h2 class="text-xl font-bold mb-4">Neues Dokument</h2>
|
||
<div class="space-y-4">
|
||
<div>
|
||
<label class="block text-sm font-medium mb-1">Titel *</label>
|
||
<input v-model="form.title" class="input" />
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium mb-1">Kategorie</label>
|
||
<select v-model="form.category_id" class="input">
|
||
<option value="">-- Wählen --</option>
|
||
<option v-for="c in categories" :key="c.id" :value="c.id">{{ c.icon }} {{ c.name }}</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium mb-1">Datei-URL</label>
|
||
<input v-model="form.file_url" class="input" placeholder="https://..." />
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium mb-1">Beschreibung</label>
|
||
<textarea v-model="form.description" rows="2" class="input"></textarea>
|
||
</div>
|
||
<label class="flex items-center">
|
||
<input v-model="form.is_mandatory" type="checkbox" class="mr-2" />
|
||
<span class="text-sm">Pflichtdokument (Bestätigung erforderlich)</span>
|
||
</label>
|
||
</div>
|
||
<div class="flex justify-end space-x-2 mt-6">
|
||
<button @click="showModal = false" class="btn">Abbrechen</button>
|
||
<button @click="createDocument" class="btn btn-primary">Erstellen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|