create DirectoryItemDetail
This commit is contained in:
parent
23d3bd35dd
commit
17b0ecf2f0
4 changed files with 177 additions and 2 deletions
173
src/components/directory/DirectoryItemDetail.vue
Normal file
173
src/components/directory/DirectoryItemDetail.vue
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { type DirectoryItem } from '@/types/directory'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import {
|
||||||
|
MapPin,
|
||||||
|
Phone,
|
||||||
|
ExternalLink,
|
||||||
|
Zap,
|
||||||
|
MessageCircle,
|
||||||
|
Facebook,
|
||||||
|
Instagram,
|
||||||
|
Twitter,
|
||||||
|
Youtube,
|
||||||
|
UtensilsCrossed,
|
||||||
|
Bed,
|
||||||
|
ShoppingBag,
|
||||||
|
Wrench,
|
||||||
|
Car,
|
||||||
|
Ship,
|
||||||
|
HelpCircle,
|
||||||
|
} from 'lucide-vue-next'
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription,
|
||||||
|
} from '@/components/ui/card'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
item: DirectoryItem
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const socials = {
|
||||||
|
facebook: Facebook,
|
||||||
|
instagram: Instagram,
|
||||||
|
twitter: Twitter,
|
||||||
|
youtube: Youtube,
|
||||||
|
}
|
||||||
|
|
||||||
|
const categoryIcons = {
|
||||||
|
restaurant: UtensilsCrossed,
|
||||||
|
lodging: Bed,
|
||||||
|
goods: ShoppingBag,
|
||||||
|
services: Wrench,
|
||||||
|
taxi: Car,
|
||||||
|
lancha: Ship,
|
||||||
|
other: HelpCircle,
|
||||||
|
} as const
|
||||||
|
|
||||||
|
const categoryColors = {
|
||||||
|
restaurant: 'text-orange-500',
|
||||||
|
lodging: 'text-purple-500',
|
||||||
|
goods: 'text-green-500',
|
||||||
|
services: 'text-pink-500',
|
||||||
|
taxi: 'text-yellow-500',
|
||||||
|
lancha: 'text-blue-500',
|
||||||
|
other: 'text-gray-500',
|
||||||
|
} as const
|
||||||
|
|
||||||
|
const socialColors = {
|
||||||
|
facebook: 'text-blue-600 hover:text-blue-700',
|
||||||
|
instagram: 'text-pink-600 hover:text-pink-700',
|
||||||
|
twitter: 'text-blue-400 hover:text-blue-500',
|
||||||
|
youtube: 'text-red-600 hover:text-red-700',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Card class="relative overflow-hidden">
|
||||||
|
<!-- Local badge -->
|
||||||
|
<div v-if="item.local"
|
||||||
|
class="absolute right-6 top-6 rounded-full bg-primary/10 px-3 py-1 text-sm font-medium text-primary">
|
||||||
|
LOCAL
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Image Banner -->
|
||||||
|
<div v-if="item.imageUrl" class="w-full h-64 overflow-hidden">
|
||||||
|
<img :src="item.imageUrl" :alt="item.name" class="w-full h-full object-cover" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CardHeader>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<component
|
||||||
|
:is="categoryIcons[item.category]"
|
||||||
|
class="h-8 w-8"
|
||||||
|
:class="categoryColors[item.category]"
|
||||||
|
:title="t(`directory.categories.${item.category}`)"
|
||||||
|
/>
|
||||||
|
<CardTitle class="text-3xl">{{ item.name }}</CardTitle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<CardDescription v-if="item.description" class="text-base mt-4">
|
||||||
|
{{ item.description }}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
<CardContent>
|
||||||
|
<div class="grid gap-6 md:grid-cols-2">
|
||||||
|
<!-- Left Column -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<!-- Location -->
|
||||||
|
<div v-if="item.town || item.address" class="flex items-start gap-3 text-base">
|
||||||
|
<MapPin class="h-5 w-5 text-red-500 mt-0.5" />
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div>{{ [item.address, item.town].filter(Boolean).join(', ') }}</div>
|
||||||
|
<a v-if="item.mapsUrl" :href="item.mapsUrl" target="_blank" rel="noopener noreferrer"
|
||||||
|
class="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors">
|
||||||
|
{{ t('directory.viewMap') }}
|
||||||
|
<ExternalLink class="h-3 w-3" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Phone -->
|
||||||
|
<div v-if="item.contact" class="flex items-start gap-3 text-base">
|
||||||
|
<Phone class="h-5 w-5 mt-0.5" />
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div>{{ item.contact }}</div>
|
||||||
|
<div v-if="item.contactType" class="flex gap-2">
|
||||||
|
<a v-if="item.contactType.includes('whatsapp')" :href="`https://wa.me/${item.contact.replace(/\D/g, '')}`"
|
||||||
|
target="_blank" rel="noopener noreferrer"
|
||||||
|
class="inline-flex items-center gap-1.5 rounded-full bg-green-100 px-3 py-1 text-sm font-medium text-green-800 hover:bg-green-200 transition-colors">
|
||||||
|
<MessageCircle class="h-4 w-4" />
|
||||||
|
WhatsApp
|
||||||
|
</a>
|
||||||
|
<a v-if="item.contactType.includes('telegram')" :href="`https://t.me/${item.contact.replace(/\D/g, '')}`"
|
||||||
|
target="_blank" rel="noopener noreferrer"
|
||||||
|
class="inline-flex items-center gap-1.5 rounded-full bg-blue-100 px-3 py-1 text-sm font-medium text-blue-800 hover:bg-blue-200 transition-colors">
|
||||||
|
<MessageCircle class="h-4 w-4" />
|
||||||
|
Telegram
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Lightning Address -->
|
||||||
|
<div v-if="item.lightning" class="flex items-center gap-3 text-base">
|
||||||
|
<Zap class="h-5 w-5 text-amber-500" />
|
||||||
|
<span>{{ item.lightning }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right Column -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<!-- Website -->
|
||||||
|
<div v-if="item.url" class="flex items-center gap-3 text-base">
|
||||||
|
<ExternalLink class="h-5 w-5" />
|
||||||
|
<a :href="item.url" target="_blank" rel="noopener noreferrer"
|
||||||
|
class="text-muted-foreground hover:text-foreground transition-colors">
|
||||||
|
{{ item.url.replace(/^https?:\/\//, '') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Social Media -->
|
||||||
|
<div v-if="item.social" class="space-y-2">
|
||||||
|
<div v-for="(url, platform) in item.social" :key="platform" class="flex items-center gap-3">
|
||||||
|
<component :is="socials[platform as keyof typeof socials]" class="h-5 w-5"
|
||||||
|
:class="socialColors[platform as keyof typeof socialColors]" />
|
||||||
|
<a :href="url" target="_blank" rel="noopener noreferrer"
|
||||||
|
class="text-muted-foreground hover:text-foreground transition-colors">
|
||||||
|
{{ platform.charAt(0).toUpperCase() + platform.slice(1) }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</template>
|
||||||
|
|
@ -39,6 +39,7 @@ export default {
|
||||||
shareTitle: 'Share this listing',
|
shareTitle: 'Share this listing',
|
||||||
copyLink: 'Copy Link',
|
copyLink: 'Copy Link',
|
||||||
linkCopied: 'Link copied to clipboard!',
|
linkCopied: 'Link copied to clipboard!',
|
||||||
|
onlinePresence: 'Online Presence',
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
poweredBy: 'Powered by',
|
poweredBy: 'Powered by',
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ export default {
|
||||||
shareTitle: 'Compartir este listado',
|
shareTitle: 'Compartir este listado',
|
||||||
copyLink: 'Copiar enlace',
|
copyLink: 'Copiar enlace',
|
||||||
linkCopied: '¡Enlace copiado al portapapeles!',
|
linkCopied: '¡Enlace copiado al portapapeles!',
|
||||||
|
onlinePresence: 'Presencia en Línea',
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
poweredBy: 'Alimentado por',
|
poweredBy: 'Alimentado por',
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { useRouter } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { Share2, Copy, Check } from 'lucide-vue-next'
|
import { Share2, Copy, Check } from 'lucide-vue-next'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import DirectoryCard from '@/components/directory/DirectoryCard.vue'
|
import DirectoryItemDetail from '@/components/directory/DirectoryItemDetail.vue'
|
||||||
import { type DirectoryItem } from '@/types/directory'
|
import { type DirectoryItem } from '@/types/directory'
|
||||||
import { mockDirectoryItems } from '@/data/directory'
|
import { mockDirectoryItems } from '@/data/directory'
|
||||||
|
|
||||||
|
|
@ -119,7 +119,7 @@ onMounted(async () => {
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<DirectoryCard :item="item" />
|
<DirectoryItemDetail :item="item" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue