124 lines
No EOL
3.7 KiB
Vue
124 lines
No EOL
3.7 KiB
Vue
<script setup lang="ts">
|
|
import { ref, onMounted, computed } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { Share2, Copy, Check } from 'lucide-vue-next'
|
|
import { Button } from '@/components/ui/button'
|
|
import DirectoryItemDetail from '@/components/directory/DirectoryItemDetail.vue'
|
|
import { type DirectoryItem } from '@/types/directory'
|
|
import { mockDirectoryItems } from '@/data/directory'
|
|
|
|
const { t } = useI18n()
|
|
|
|
const props = defineProps<{
|
|
id: string
|
|
}>()
|
|
|
|
const item = ref<DirectoryItem | null>(null)
|
|
const loading = ref(true)
|
|
const error = ref(false)
|
|
const justCopied = ref(false)
|
|
|
|
// Check if Web Share API is available
|
|
const canShare = computed(() => typeof navigator !== 'undefined' && !!navigator.share)
|
|
|
|
// Share functionality
|
|
const shareItem = async () => {
|
|
if (!item.value) return
|
|
|
|
const shareData = {
|
|
title: item.value.name,
|
|
text: item.value.description || `Check out ${item.value.name} on Atitlan Directory`,
|
|
url: window.location.href
|
|
}
|
|
|
|
if (canShare.value) {
|
|
try {
|
|
await navigator.share(shareData)
|
|
} catch (err) {
|
|
if (err instanceof Error && err.name !== 'AbortError') {
|
|
console.error('Error sharing:', err)
|
|
}
|
|
}
|
|
} else {
|
|
// Fallback to copying the URL
|
|
await copyToClipboard()
|
|
}
|
|
}
|
|
|
|
// Copy to clipboard functionality
|
|
const copyToClipboard = async () => {
|
|
try {
|
|
await navigator.clipboard.writeText(window.location.href)
|
|
justCopied.value = true
|
|
setTimeout(() => {
|
|
justCopied.value = false
|
|
}, 2000)
|
|
} catch (err) {
|
|
console.error('Failed to copy:', err)
|
|
}
|
|
}
|
|
|
|
onMounted(async () => {
|
|
try {
|
|
const found = mockDirectoryItems.find(i => i.id === props.id)
|
|
if (!found) {
|
|
error.value = true
|
|
return
|
|
}
|
|
|
|
item.value = found
|
|
} catch (e) {
|
|
error.value = true
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="container mx-auto px-4 py-8">
|
|
<div class="max-w-2xl mx-auto">
|
|
<!-- Loading State -->
|
|
<div v-if="loading" class="text-center py-12">
|
|
<div class="animate-pulse">
|
|
<div class="h-8 bg-muted rounded w-3/4 mx-auto mb-4"></div>
|
|
<div class="h-4 bg-muted rounded w-1/2 mx-auto"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error State -->
|
|
<div v-else-if="error" class="text-center py-12">
|
|
<h2 class="text-xl font-semibold mb-2">{{ t('directory.itemNotFound') }}</h2>
|
|
<p class="text-muted-foreground mb-4">{{ t('directory.itemNotFoundDesc') }}</p>
|
|
<router-link to="/directory"
|
|
class="inline-flex items-center justify-center rounded-md bg-primary px-6 py-3 text-sm font-medium text-primary-foreground hover:bg-primary/90">
|
|
{{ t('directory.backToDirectory') }}
|
|
</router-link>
|
|
</div>
|
|
|
|
<!-- Directory Item -->
|
|
<template v-else-if="item">
|
|
<div class="mb-6 flex justify-between items-center">
|
|
<router-link to="/directory"
|
|
class="text-sm text-muted-foreground hover:text-foreground transition-colors">
|
|
← {{ t('directory.backToDirectory') }}
|
|
</router-link>
|
|
|
|
<!-- Share Button -->
|
|
<Button variant="outline" size="sm" @click="shareItem">
|
|
<template v-if="canShare">
|
|
<Share2 class="h-4 w-4 mr-2" />
|
|
{{ t('directory.share') }}
|
|
</template>
|
|
<template v-else>
|
|
<Copy v-if="!justCopied" class="h-4 w-4 mr-2" />
|
|
<Check v-else class="h-4 w-4 mr-2" />
|
|
{{ justCopied ? t('directory.linkCopied') : t('directory.copyLink') }}
|
|
</template>
|
|
</Button>
|
|
</div>
|
|
<DirectoryItemDetail :item="item" />
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template> |