Corregir componentes de contactos y filtrar solo con teléfono
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m0s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m0s
- Renombrar componentes: ContactsFilters→Filters, ContactItem→Item, ContactsList→List - Actualizar referencias en List.vue y app.vue - Filtrar solo contactos con teléfono registrado - Los filtros ahora deberían mostrarse correctamente
This commit is contained in:
317
nuxt4/app/components/contacts/Item.vue
Normal file
317
nuxt4/app/components/contacts/Item.vue
Normal file
@@ -0,0 +1,317 @@
|
||||
<template>
|
||||
<div
|
||||
class="contact-item"
|
||||
:class="{ 'contact-item-clickable': contact.telefono }"
|
||||
@click="handleClick"
|
||||
>
|
||||
<div class="contact-content">
|
||||
<!-- Avatar -->
|
||||
<div class="contact-avatar">
|
||||
<img
|
||||
v-if="contact.avatar_url && !avatarError"
|
||||
:src="contact.avatar_url"
|
||||
:alt="displayName"
|
||||
class="avatar-img"
|
||||
@error="avatarError = true"
|
||||
/>
|
||||
<UIcon v-else name="i-heroicons-user-circle" class="avatar-icon" />
|
||||
</div>
|
||||
|
||||
<!-- Info -->
|
||||
<div class="contact-info">
|
||||
<div class="contact-name-row">
|
||||
<span class="contact-name">{{ displayName }}</span>
|
||||
<UBadge v-if="hasAlias" size="xs" color="primary" variant="soft">
|
||||
Alias
|
||||
</UBadge>
|
||||
</div>
|
||||
<span v-if="contact.telefono" class="contact-phone">
|
||||
<UIcon name="i-heroicons-phone" class="phone-icon" />
|
||||
{{ contact.telefono }}
|
||||
</span>
|
||||
<span v-if="contact.ubicacion" class="contact-location">
|
||||
<UIcon name="i-heroicons-map-pin" class="location-icon" />
|
||||
{{ contact.ubicacion }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Acciones -->
|
||||
<div class="contact-actions" @click.stop>
|
||||
<UButton
|
||||
icon="i-heroicons-pencil-square"
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
color="neutral"
|
||||
@click="openAliasModal"
|
||||
title="Editar alias"
|
||||
/>
|
||||
<UIcon
|
||||
v-if="contact.telefono"
|
||||
name="i-heroicons-chat-bubble-left-ellipsis"
|
||||
class="whatsapp-icon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal para editar alias -->
|
||||
<UModal v-model:open="showAliasModal">
|
||||
<template #content>
|
||||
<div class="alias-modal">
|
||||
<h3 class="alias-modal-title">Editar alias</h3>
|
||||
<p class="alias-modal-original">
|
||||
<span class="label">Nombre original:</span>
|
||||
{{ contact.name }}
|
||||
</p>
|
||||
<UFormField label="Alias personalizado">
|
||||
<UInput
|
||||
v-model="newAlias"
|
||||
placeholder="Escribe un alias..."
|
||||
maxlength="50"
|
||||
class="alias-input"
|
||||
/>
|
||||
</UFormField>
|
||||
<p class="alias-modal-hint">
|
||||
Máximo 50 caracteres. Deja vacío para eliminar el alias.
|
||||
</p>
|
||||
<div class="alias-modal-actions">
|
||||
<UButton variant="ghost" color="neutral" @click="showAliasModal = false">
|
||||
Cancelar
|
||||
</UButton>
|
||||
<UButton :loading="saving" @click="saveAlias">
|
||||
Guardar
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Contact } from '~/composables/useContacts'
|
||||
|
||||
const props = defineProps<{
|
||||
contact: Contact
|
||||
alias?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update-alias': [{ contactId: number; alias: string }]
|
||||
'click': []
|
||||
}>()
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
// Estado local
|
||||
const avatarError = ref(false)
|
||||
const showAliasModal = ref(false)
|
||||
const newAlias = ref('')
|
||||
const saving = ref(false)
|
||||
|
||||
// Computed
|
||||
const displayName = computed(() => props.alias || props.contact.name)
|
||||
const hasAlias = computed(() => !!props.alias)
|
||||
|
||||
// Métodos
|
||||
const handleClick = () => {
|
||||
if (props.contact.telefono) {
|
||||
emit('click')
|
||||
}
|
||||
}
|
||||
|
||||
const openAliasModal = () => {
|
||||
newAlias.value = props.alias || ''
|
||||
showAliasModal.value = true
|
||||
}
|
||||
|
||||
const saveAlias = async () => {
|
||||
saving.value = true
|
||||
try {
|
||||
emit('update-alias', {
|
||||
contactId: props.contact.id,
|
||||
alias: newAlias.value.trim()
|
||||
})
|
||||
showAliasModal.value = false
|
||||
toast.add({
|
||||
title: newAlias.value.trim() ? 'Alias guardado' : 'Alias eliminado',
|
||||
color: 'success'
|
||||
})
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.contact-item {
|
||||
padding: 1rem;
|
||||
border-radius: 1rem;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
backdrop-filter: blur(15px) saturate(180%);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.contact-item-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.contact-item-clickable:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 24px 0 rgba(var(--color-primary-500), 0.2);
|
||||
border-color: rgba(var(--color-primary-500), 0.4);
|
||||
}
|
||||
|
||||
.contact-content {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contact-avatar {
|
||||
flex-shrink: 0;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border-radius: 50%;
|
||||
background: rgba(var(--color-primary-500), 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.avatar-icon {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
color: rgb(var(--color-primary-500));
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.contact-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.contact-name {
|
||||
font-weight: 600;
|
||||
color: var(--color-gray-900);
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
.contact-phone,
|
||||
.contact-location {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
font-size: 0.8125rem;
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
|
||||
.phone-icon,
|
||||
.location-icon {
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.contact-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.whatsapp-icon {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
color: #25D366;
|
||||
}
|
||||
|
||||
/* Modal de alias */
|
||||
.alias-modal {
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.alias-modal-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-gray-900);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.alias-modal-original {
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-gray-600);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.alias-modal-original .label {
|
||||
font-weight: 500;
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
|
||||
.alias-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.alias-modal-hint {
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-gray-500);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.alias-modal-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.75rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* Modo oscuro */
|
||||
.dark .contact-item {
|
||||
background: rgba(0, 0, 0, 0.3) !important;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
.dark .contact-item-clickable:hover {
|
||||
box-shadow: 0 8px 24px 0 rgba(var(--color-primary-500), 0.3) !important;
|
||||
border-color: rgba(var(--color-primary-500), 0.5) !important;
|
||||
}
|
||||
|
||||
.dark .contact-name {
|
||||
color: var(--color-gray-100) !important;
|
||||
}
|
||||
|
||||
.dark .contact-phone,
|
||||
.dark .contact-location {
|
||||
color: var(--color-gray-400) !important;
|
||||
}
|
||||
|
||||
.dark .alias-modal-title {
|
||||
color: var(--color-gray-100) !important;
|
||||
}
|
||||
|
||||
.dark .alias-modal-original .label {
|
||||
color: var(--color-gray-300) !important;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user