174 lines
5.2 KiB
Vue
174 lines
5.2 KiB
Vue
<script setup lang="ts">
|
|
import { useStaffTime } from '~/composables/useStaffTime'
|
|
import { useAuthStore } from '~/stores/auth'
|
|
|
|
const { list, start, stop } = useStaffTime()
|
|
const auth = useAuthStore()
|
|
const router = useRouter()
|
|
|
|
const entries = ref([])
|
|
const active = computed(() => entries.value.find(e => !e.stopped_at && e.user_id === auth.user.id))
|
|
const loading = ref(false)
|
|
const showModal = ref(false)
|
|
const editEntry = ref(null)
|
|
|
|
// 👥 Nutzer-Filter (nur für Berechtigte)
|
|
const users = ref([])
|
|
const selectedUser = ref<string | null>(null)
|
|
|
|
const canViewAll = computed(() => auth.permissions.includes('staff.time.read_all'))
|
|
|
|
async function loadUsers() {
|
|
if (!canViewAll.value) return
|
|
// Beispiel: User aus Supabase holen
|
|
const res = (await useNuxtApp().$api("/api/tenant/profiles")).data
|
|
users.value = res
|
|
}
|
|
|
|
async function load() {
|
|
entries.value = await list(
|
|
canViewAll.value && selectedUser.value ? { user_id: selectedUser.value } : undefined
|
|
)
|
|
}
|
|
|
|
async function handleStart() {
|
|
loading.value = true
|
|
await start('Arbeitszeit gestartet')
|
|
await load()
|
|
loading.value = false
|
|
}
|
|
|
|
async function handleStop() {
|
|
if (!active.value) return
|
|
loading.value = true
|
|
await stop(active.value.id)
|
|
await load()
|
|
loading.value = false
|
|
}
|
|
|
|
function handleEdit(entry: any) {
|
|
editEntry.value = entry
|
|
showModal.value = true
|
|
}
|
|
|
|
onMounted(async () => {
|
|
await loadUsers()
|
|
await load()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<UDashboardNavbar title="Zeiterfassung" :badge="entries.length" />
|
|
|
|
<!-- TOOLBAR -->
|
|
<UDashboardToolbar>
|
|
<template #left>
|
|
<div class="flex items-center gap-2">
|
|
<UIcon name="i-heroicons-clock" class="text-primary-500" />
|
|
<span v-if="active" class="text-primary-600 font-medium">
|
|
Läuft seit {{ useNuxtApp().$dayjs(active.started_at).format('HH:mm') }}
|
|
</span>
|
|
<span v-else class="text-gray-500">Keine aktive Zeit</span>
|
|
</div>
|
|
</template>
|
|
|
|
<template #right>
|
|
<UButton
|
|
v-if="active"
|
|
color="red"
|
|
icon="i-heroicons-stop"
|
|
:loading="loading"
|
|
label="Stoppen"
|
|
@click="handleStop"
|
|
/>
|
|
<UButton
|
|
v-else
|
|
color="green"
|
|
icon="i-heroicons-play"
|
|
:loading="loading"
|
|
label="Starten"
|
|
@click="handleStart"
|
|
/>
|
|
<UButton
|
|
color="primary"
|
|
icon="i-heroicons-plus"
|
|
label="Zeit"
|
|
@click="() => { editEntry = null; showModal = true }"
|
|
/>
|
|
</template>
|
|
</UDashboardToolbar>
|
|
<UDashboardToolbar>
|
|
<template #left>
|
|
<!-- 👥 User-Filter (nur bei Berechtigung) -->
|
|
<div v-if="canViewAll" class="flex items-center gap-2">
|
|
<USelectMenu
|
|
v-model="selectedUser"
|
|
:options="[
|
|
{ label: 'Alle Benutzer', value: null },
|
|
...users.map(u => ({ label: u.full_name || u.email, value: u.user_id }))
|
|
]"
|
|
placeholder="Benutzer auswählen"
|
|
value-attribute="value"
|
|
option-attribute="label"
|
|
class="min-w-[220px]"
|
|
@change="load"
|
|
/>
|
|
|
|
<!-- 🔹 Button zur Auswertung -->
|
|
<UButton
|
|
v-if="selectedUser"
|
|
color="gray"
|
|
icon="i-heroicons-chart-bar"
|
|
label="Auswertung"
|
|
variant="soft"
|
|
@click="router.push(`/staff/time/${selectedUser}/evaluate`)"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</UDashboardToolbar>
|
|
|
|
<!-- TABELLE -->
|
|
<UDashboardPanelContent>
|
|
<UTable
|
|
:rows="entries"
|
|
@select="(row) => handleEdit(row)"
|
|
:columns="[
|
|
{ key: 'state', label: 'Status' },
|
|
{ key: 'started_at', label: 'Start' },
|
|
{ key: 'stopped_at', label: 'Ende' },
|
|
{ key: 'duration_minutes', label: 'Dauer' },
|
|
{ key: 'description', label: 'Beschreibung' },
|
|
...(canViewAll ? [{ key: 'user_name', label: 'Benutzer' }] : []),
|
|
{ key: 'actions', label: '' },
|
|
]"
|
|
>
|
|
<template #state-data="{row}">
|
|
<span v-if="row.state === 'approved'" class="text-primary-500">Genehmigt</span>
|
|
<span v-else-if="row.state === 'submitted'" class="text-cyan-500">Eingereicht</span>
|
|
<span v-else-if="row.state === 'draft'" class="text-red-500">Entwurf</span>
|
|
</template>
|
|
<template #started_at-data="{ row }">
|
|
{{ useNuxtApp().$dayjs(row.started_at).format('DD.MM.YY HH:mm') }}
|
|
</template>
|
|
<template #stopped_at-data="{ row }">
|
|
<span v-if="row.stopped_at">
|
|
{{ useNuxtApp().$dayjs(row.stopped_at).format('DD.MM.YY HH:mm') }}
|
|
</span>
|
|
<span v-else class="text-primary-500 font-medium">läuft...</span>
|
|
</template>
|
|
<template #duration_minutes-data="{ row }">
|
|
{{ row.duration_minutes ? useFormatDuration(row.duration_minutes) : '-' }}
|
|
</template>
|
|
<template #user_name-data="{ row }">
|
|
{{ row.user_id ? users.find(i => i.user_id === row.user_id).full_name : '-' }}
|
|
</template>
|
|
<template #actions-data="{ row }">
|
|
<UButton variant="ghost" icon="i-heroicons-pencil-square" @click="handleEdit(row)" />
|
|
</template>
|
|
</UTable>
|
|
</UDashboardPanelContent>
|
|
|
|
<!-- MODAL -->
|
|
<StaffTimeEntryModal v-model="showModal" :entry="editEntry" @saved="load" />
|
|
</template>
|