feat: Full responsive design - mobile sidebar, card lists, modals
This commit is contained in:
@@ -46,14 +46,14 @@ function getInitials(contact) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-6">
|
||||
<div class="p-4 sm:p-6">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-white">Kontakte</h1>
|
||||
<p class="text-pulse-muted">{{ contacts.meta.total }} Kontakte</p>
|
||||
<h1 class="text-xl sm:text-2xl font-bold text-white">Kontakte</h1>
|
||||
<p class="text-pulse-muted text-sm">{{ contacts.meta.total }} Kontakte</p>
|
||||
</div>
|
||||
<button @click="showNewModal = true" class="btn-primary">
|
||||
<button @click="showNewModal = true" class="btn-primary w-full sm:w-auto">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
@@ -63,21 +63,21 @@ function getInitials(contact) {
|
||||
|
||||
<!-- Search -->
|
||||
<div class="mb-6">
|
||||
<div class="relative max-w-md">
|
||||
<div class="relative w-full sm:max-w-md">
|
||||
<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-pulse-muted" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
<input
|
||||
v-model="search"
|
||||
type="text"
|
||||
class="input pl-10"
|
||||
class="input pl-10 w-full"
|
||||
placeholder="Suchen..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="card">
|
||||
<!-- Desktop Table -->
|
||||
<div class="card hidden md:block">
|
||||
<div class="table-container">
|
||||
<table class="table">
|
||||
<thead>
|
||||
@@ -98,7 +98,7 @@ function getInitials(contact) {
|
||||
>
|
||||
<td>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-9 h-9 rounded-full bg-primary-600 flex items-center justify-center text-white text-sm font-medium">
|
||||
<div class="w-9 h-9 rounded-full bg-primary-600 flex items-center justify-center text-white text-sm font-medium flex-shrink-0">
|
||||
{{ getInitials(contact) }}
|
||||
</div>
|
||||
<div>
|
||||
@@ -117,29 +117,84 @@ function getInitials(contact) {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div v-if="!contacts.loading && !contacts.contacts.length" class="p-12 text-center">
|
||||
<svg class="w-16 h-16 mx-auto mb-4 text-pulse-muted opacity-50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
|
||||
</svg>
|
||||
<p class="text-pulse-muted">Noch keine Kontakte vorhanden</p>
|
||||
<button @click="showNewModal = true" class="btn-primary mt-4">
|
||||
Ersten Kontakt anlegen
|
||||
</button>
|
||||
<!-- Mobile Card List -->
|
||||
<div class="md:hidden space-y-3">
|
||||
<div
|
||||
v-for="contact in contacts.contacts"
|
||||
:key="contact.id"
|
||||
class="card p-4 cursor-pointer active:scale-[0.98] transition-transform"
|
||||
@click="router.push(`/contacts/${contact.id}`)"
|
||||
>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-10 h-10 rounded-full bg-primary-600 flex items-center justify-center text-white text-sm font-medium flex-shrink-0">
|
||||
{{ getInitials(contact) }}
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="font-medium text-white">{{ contact.firstName }} {{ contact.lastName }}</p>
|
||||
<p v-if="contact.position" class="text-sm text-pulse-muted">{{ contact.position }}</p>
|
||||
<p v-if="contact.company" class="text-sm text-primary-400">{{ contact.company.name }}</p>
|
||||
<div class="mt-2 flex flex-wrap gap-2">
|
||||
<a
|
||||
v-if="contact.email"
|
||||
:href="`mailto:${contact.email}`"
|
||||
@click.stop
|
||||
class="inline-flex items-center gap-1 text-xs text-pulse-muted hover:text-white px-2 py-1 bg-pulse-dark rounded"
|
||||
>
|
||||
<svg class="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||
</svg>
|
||||
{{ contact.email }}
|
||||
</a>
|
||||
<a
|
||||
v-if="contact.phone"
|
||||
:href="`tel:${contact.phone}`"
|
||||
@click.stop
|
||||
class="inline-flex items-center gap-1 text-xs text-pulse-muted hover:text-white px-2 py-1 bg-pulse-dark rounded"
|
||||
>
|
||||
<svg class="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||
</svg>
|
||||
{{ contact.phone }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<svg class="w-5 h-5 text-pulse-muted flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div v-if="!contacts.loading && !contacts.contacts.length" class="card p-8 sm:p-12 text-center">
|
||||
<svg class="w-12 sm:w-16 h-12 sm:h-16 mx-auto mb-4 text-pulse-muted opacity-50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
|
||||
</svg>
|
||||
<p class="text-pulse-muted">Noch keine Kontakte vorhanden</p>
|
||||
<button @click="showNewModal = true" class="btn-primary mt-4">
|
||||
Ersten Kontakt anlegen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- New Contact Modal -->
|
||||
<Teleport to="body">
|
||||
<div v-if="showNewModal" class="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div v-if="showNewModal" class="fixed inset-0 z-50 flex items-end sm:items-center justify-center p-0 sm:p-4">
|
||||
<div class="absolute inset-0 bg-black/60" @click="showNewModal = false"></div>
|
||||
<div class="relative card w-full max-w-lg mx-4">
|
||||
<div class="px-6 py-4 border-b border-pulse-border">
|
||||
<h2 class="text-lg font-semibold text-white">Neuer Kontakt</h2>
|
||||
<div class="relative card w-full sm:max-w-lg rounded-b-none sm:rounded-b-xl max-h-[90vh] overflow-y-auto">
|
||||
<div class="px-4 sm:px-6 py-4 border-b border-pulse-border sticky top-0 bg-pulse-card z-10">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold text-white">Neuer Kontakt</h2>
|
||||
<button @click="showNewModal = false" class="p-2 text-pulse-muted hover:text-white sm:hidden">
|
||||
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<form @submit.prevent="createContact" class="p-6 space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<form @submit.prevent="createContact" class="p-4 sm:p-6 space-y-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="label">Vorname *</label>
|
||||
<input v-model="newContact.firstName" type="text" class="input" required />
|
||||
@@ -161,7 +216,7 @@ function getInitials(contact) {
|
||||
<label class="label">Position</label>
|
||||
<input v-model="newContact.position" type="text" class="input" placeholder="z.B. Geschäftsführer" />
|
||||
</div>
|
||||
<div class="flex gap-3 pt-2">
|
||||
<div class="flex flex-col-reverse sm:flex-row gap-3 pt-2">
|
||||
<button type="button" @click="showNewModal = false" class="btn-secondary flex-1">
|
||||
Abbrechen
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user