mirror of
https://github.com/abhinavxd/libredesk.git
synced 2025-11-01 20:44:12 +00:00
use tabs for search results seperation also looks better now.
This commit is contained in:
@@ -1,105 +1,139 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="max-w-5xl mx-auto p-6 min-h-screen">
|
<div class="max-w-5xl mx-auto p-6 min-h-screen">
|
||||||
<div class="space-y-8">
|
<Tabs :default-value="defaultTab" v-model="activeTab">
|
||||||
<div
|
<TabsList class="grid w-full mb-6" :class="tabsGridClass">
|
||||||
v-for="(items, type) in results"
|
<TabsTrigger v-for="(items, type) in results" :key="type" :value="type" class="capitalize">
|
||||||
:key="type"
|
{{ type }} ({{ items.length }})
|
||||||
class="bg-card rounded shadow overflow-hidden"
|
</TabsTrigger>
|
||||||
>
|
</TabsList>
|
||||||
<!-- Header for each section -->
|
|
||||||
<h2
|
|
||||||
class="bg-primary dark:bg-primary text-lg font-bold text-white dark:text-primary-foreground py-2 px-6 capitalize"
|
|
||||||
>
|
|
||||||
{{ type }}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<!-- No results message -->
|
<TabsContent v-for="(items, type) in results" :key="type" :value="type" class="mt-0">
|
||||||
<div v-if="items.length === 0" class="p-6 text-gray-500 dark:text-muted-foreground">
|
<div class="bg-background rounded border overflow-hidden">
|
||||||
{{
|
<!-- No results message -->
|
||||||
$t('globals.messages.noResults', {
|
<div v-if="items.length === 0" class="p-8 text-center text-muted-foreground">
|
||||||
name: type
|
<div class="text-lg font-medium mb-2">
|
||||||
})
|
{{
|
||||||
}}
|
$t('globals.messages.noResults', {
|
||||||
</div>
|
name: type
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div class="text-sm">{{ $t('search.adjustSearchTerms') }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Results list -->
|
<!-- Results list -->
|
||||||
<div class="divide-y divide-gray-200 dark:divide-border">
|
<div v-else class="divide-y divide-border">
|
||||||
<div
|
<div
|
||||||
v-for="item in items"
|
v-for="item in items"
|
||||||
:key="item.id || item.uuid"
|
:key="item.id || item.uuid"
|
||||||
class="p-6 hover:bg-gray-100 dark:hover:bg-accent transition duration-300 ease-in-out group"
|
class="p-6 hover:bg-accent/50 transition duration-200 ease-in-out group"
|
||||||
>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: 'inbox-conversation',
|
|
||||||
params: {
|
|
||||||
uuid: type === 'conversations' ? item.uuid : item.conversation_uuid,
|
|
||||||
type: 'assigned'
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
class="block"
|
|
||||||
>
|
>
|
||||||
<div class="flex justify-between items-start">
|
<router-link
|
||||||
<div class="flex-grow">
|
:to="{
|
||||||
<!-- Reference number -->
|
name: 'inbox-conversation',
|
||||||
<div
|
params: {
|
||||||
class="text-sm font-semibold mb-2 group-hover:text-primary dark:group-hover:text-primary transition duration-300"
|
uuid: type === 'conversations' ? item.uuid : item.conversation_uuid,
|
||||||
>
|
type: 'assigned'
|
||||||
#{{
|
}
|
||||||
type === 'conversations'
|
}"
|
||||||
? item.reference_number
|
class="block"
|
||||||
: item.conversation_reference_number
|
>
|
||||||
}}
|
<div class="flex justify-between items-start">
|
||||||
|
<div class="flex-grow">
|
||||||
|
<!-- Reference number -->
|
||||||
|
<div
|
||||||
|
class="text-sm font-semibold mb-2 text-muted-foreground group-hover:text-primary transition duration-200"
|
||||||
|
>
|
||||||
|
#{{
|
||||||
|
type === 'conversations'
|
||||||
|
? item.reference_number
|
||||||
|
: item.conversation_reference_number
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<div
|
||||||
|
class="text-foreground font-medium mb-2 text-lg group-hover:text-primary transition duration-200"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
truncateText(
|
||||||
|
type === 'conversations' ? item.subject : item.text_content,
|
||||||
|
100
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Timestamp -->
|
||||||
|
<div class="text-sm text-muted-foreground flex items-center">
|
||||||
|
<ClockIcon class="h-4 w-4 mr-1" />
|
||||||
|
{{
|
||||||
|
formatDate(
|
||||||
|
type === 'conversations' ? item.created_at : item.conversation_created_at
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Content -->
|
<!-- Right arrow icon -->
|
||||||
<div
|
<div
|
||||||
class="text-gray-900 dark:text-card-foreground font-medium mb-2 text-lg group-hover:text-gray-950 dark:group-hover:text-foreground transition duration-300"
|
class="bg-secondary rounded-full p-2 group-hover:bg-primary transition duration-200"
|
||||||
>
|
>
|
||||||
{{
|
<ChevronRightIcon
|
||||||
truncateText(type === 'conversations' ? item.subject : item.text_content, 100)
|
class="h-5 w-5 text-secondary-foreground group-hover:text-primary-foreground"
|
||||||
}}
|
aria-hidden="true"
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
<!-- Timestamp -->
|
|
||||||
<div class="text-sm text-gray-500 dark:text-muted-foreground flex items-center">
|
|
||||||
<ClockIcon class="h-4 w-4 mr-1" />
|
|
||||||
{{
|
|
||||||
formatDate(
|
|
||||||
type === 'conversations' ? item.created_at : item.conversation_created_at
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</router-link>
|
||||||
<!-- Right arrow icon -->
|
</div>
|
||||||
<div
|
|
||||||
class="bg-gray-200 dark:bg-secondary rounded-full p-2 group-hover:bg-primary dark:group-hover:bg-primary transition duration-300"
|
|
||||||
>
|
|
||||||
<ChevronRightIcon
|
|
||||||
class="h-5 w-5 text-gray-700 dark:text-secondary-foreground group-hover:text-white dark:group-hover:text-primary-foreground"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</router-link>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</TabsContent>
|
||||||
</div>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { computed, ref, watch } from 'vue'
|
||||||
import { ChevronRightIcon, ClockIcon } from 'lucide-vue-next'
|
import { ChevronRightIcon, ClockIcon } from 'lucide-vue-next'
|
||||||
import { format, parseISO } from 'date-fns'
|
import { format, parseISO } from 'date-fns'
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||||
|
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
results: {
|
results: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Get the first available tab as default
|
||||||
|
const defaultTab = computed(() => {
|
||||||
|
const types = Object.keys(props.results)
|
||||||
|
return types.length > 0 ? types[0] : ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const activeTab = ref('')
|
||||||
|
|
||||||
|
// Watch for changes in results and set the first tab as active
|
||||||
|
watch(
|
||||||
|
() => props.results,
|
||||||
|
(newResults) => {
|
||||||
|
const types = Object.keys(newResults)
|
||||||
|
if (types.length > 0 && !activeTab.value) {
|
||||||
|
activeTab.value = types[0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// Dynamic grid class based on number of tabs
|
||||||
|
const tabsGridClass = computed(() => {
|
||||||
|
const tabCount = Object.keys(props.results).length
|
||||||
|
if (tabCount <= 2) return 'grid-cols-2'
|
||||||
|
if (tabCount <= 3) return 'grid-cols-3'
|
||||||
|
if (tabCount <= 4) return 'grid-cols-4'
|
||||||
|
return 'grid-cols-5'
|
||||||
|
})
|
||||||
|
|
||||||
const formatDate = (dateString) => {
|
const formatDate = (dateString) => {
|
||||||
const date = parseISO(dateString)
|
const date = parseISO(dateString)
|
||||||
return format(date, 'MMM d, yyyy HH:mm')
|
return format(date, 'MMM d, yyyy HH:mm')
|
||||||
|
|||||||
@@ -568,6 +568,7 @@
|
|||||||
"search.noResultsForQuery": "No results found for query `{query}`. Try a different search term.",
|
"search.noResultsForQuery": "No results found for query `{query}`. Try a different search term.",
|
||||||
"search.minQueryLength": " Please enter at least {length} characters to search.",
|
"search.minQueryLength": " Please enter at least {length} characters to search.",
|
||||||
"search.searchBy": "Search by reference number, contact email address or messages in conversations.",
|
"search.searchBy": "Search by reference number, contact email address or messages in conversations.",
|
||||||
|
"search.adjustSearchTerms": "Try adjusting your search terms or filters.",
|
||||||
"sla.overdueBy": "Overdue by",
|
"sla.overdueBy": "Overdue by",
|
||||||
"sla.met": "SLA met",
|
"sla.met": "SLA met",
|
||||||
"view.form.description": "Create and save custom filter views for quick access to your conversations.",
|
"view.form.description": "Create and save custom filter views for quick access to your conversations.",
|
||||||
|
|||||||
Reference in New Issue
Block a user