use tabs for search results seperation also looks better now.

This commit is contained in:
Abhinav Raut
2025-08-14 16:12:29 +05:30
parent 69accaebef
commit b971619ea6
2 changed files with 112 additions and 77 deletions

View File

@@ -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')

View File

@@ -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.",