feat: Add all module frontend views

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
This commit is contained in:
2026-03-12 21:23:01 +00:00
parent e5d09e9c80
commit 3ca75cc4f2
39 changed files with 1272 additions and 700 deletions

137
src/views/DocumentsView.vue Normal file
View File

@@ -0,0 +1,137 @@
<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>