WIP - saving list views

This commit is contained in:
Francisco Gaona
2026-04-10 10:37:11 +02:00
parent a0bdb09c03
commit 12304d5890
15 changed files with 974 additions and 1 deletions

View File

@@ -17,9 +17,17 @@ import { Input } from '@/components/ui/input'
import { Badge } from '@/components/ui/badge'
import { Checkbox } from '@/components/ui/checkbox'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import FieldRenderer from '@/components/fields/FieldRenderer.vue'
import { ListViewConfig, ViewMode, FieldType, FieldConfig } from '@/types/field-types'
import { ChevronDown, ChevronUp, Search, Plus, Download, Trash2, Edit } from 'lucide-vue-next'
import { ChevronDown, ChevronUp, Search, Plus, Download, Trash2, Edit, Bookmark, BookmarkPlus, Settings2 } from 'lucide-vue-next'
import type { SavedView } from '@/composables/useSavedViews'
interface Props {
config: ListViewConfig
@@ -32,6 +40,11 @@ interface Props {
draftEdits?: Record<string, Record<string, any>>
cellErrors?: Record<string, Record<string, string | boolean>>
savingDrafts?: boolean
// Saved views
savedViews?: SavedView[]
activeViewId?: string | null
currentSearchPlan?: { strategy: string; filters: any[]; sort: any; explanation: string } | null
savingView?: boolean
}
const props = withDefaults(defineProps<Props>(), {
@@ -43,6 +56,10 @@ const props = withDefaults(defineProps<Props>(), {
draftEdits: () => ({}),
cellErrors: () => ({}),
savingDrafts: false,
savedViews: () => [],
activeViewId: null,
currentSearchPlan: null,
savingView: false,
})
const emit = defineEmits<{
@@ -61,6 +78,10 @@ const emit = defineEmits<{
'cell-edit': [payload: { row: any; field: FieldConfig; newValue: any; oldValue: any }]
'save-drafts': []
'discard-drafts': []
// Saved views
'apply-view': [view: SavedView]
'save-view': []
'open-view-manager': []
}>()
// State
@@ -399,6 +420,66 @@ watch(
</div>
<div class="flex items-center gap-2">
<!-- Saved Views dropdown + cog -->
<div class="flex items-center gap-1">
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button variant="outline" size="sm" class="gap-2">
<Bookmark class="h-4 w-4" />
<span class="max-w-[120px] truncate">
{{ savedViews.find(v => v.id === activeViewId)?.name || 'Views' }}
</span>
<ChevronDown class="h-3 w-3 opacity-60" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" class="w-56">
<DropdownMenuItem
v-if="savedViews.length === 0"
disabled
class="text-muted-foreground"
>
No saved views yet
</DropdownMenuItem>
<DropdownMenuItem
v-for="view in savedViews"
:key="view.id"
:class="{ 'font-medium': view.id === activeViewId }"
@click="emit('apply-view', view)"
>
<span class="flex-1 truncate">{{ view.name }}</span>
<Badge v-if="view.isShared" variant="secondary" class="ml-2 text-[10px] px-1.5 py-0">Shared</Badge>
</DropdownMenuItem>
<DropdownMenuSeparator v-if="savedViews.length > 0" />
<DropdownMenuItem @click="emit('open-view-manager')">
Manage views
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Button
variant="ghost"
size="icon"
class="h-8 w-8"
title="Manage saved views"
@click="emit('open-view-manager')"
>
<Settings2 class="h-4 w-4" />
</Button>
</div>
<!-- Save current search as a view (only for query strategy) -->
<Button
v-if="currentSearchPlan?.strategy === 'query'"
variant="outline"
size="sm"
:disabled="savingView"
class="gap-2"
@click="emit('save-view')"
>
<BookmarkPlus class="h-4 w-4" />
Save view
</Button>
<Select v-model="viewMode">
<SelectTrigger class="h-8 w-[180px]">
<SelectValue placeholder="Select view" />