Sticky table headers, layout parity, and reuse My Tasks on the shared table/toolbar
Shot/Asset/Task data tables now keep their header row pinned while scrolling (both the locked two-pane and single-table modes), matching a common data- table expectation that was missing everywhere. Brings Asset and Task browsers to full structural parity with Shot's bounded- height layout (fixed toolbar, internally-scrolling table) instead of their previous page-scroll model with a sticky-positioned toolbar. AssetsDataTable gained the synced frozen/movable-pane scrolling it was missing entirely; TasksDataTable and both browsers/parent views got the same container model. The global My Tasks page (/tasks) now reuses TaskTableToolbar and TasksDataTable instead of its own ad hoc filter selects and TaskList, including the detail panel, bulk status/assignment context menu, and sticky header for free. Since it spans multiple projects, TaskTableToolbar gained an optional Project filter (only rendered when a project list is passed in, so the project-scoped Tasks page is unaffected); episode/assignee filters populate once a specific project is chosen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -92,6 +92,49 @@
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
<!-- Project Filter (only shown when a cross-project task list is passed in, e.g. My Tasks) -->
|
||||
<Popover v-if="projects && projects.length > 0">
|
||||
<PopoverTrigger as-child>
|
||||
<Button variant="outline" size="sm" class="h-8 border-dashed">
|
||||
<FolderOpen class="mr-2 h-4 w-4" />
|
||||
Project
|
||||
<Badge
|
||||
v-if="projectFilter !== null"
|
||||
variant="secondary"
|
||||
class="ml-2 rounded-sm px-1 font-normal"
|
||||
>
|
||||
1
|
||||
</Badge>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-[200px] p-0" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search project..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No project found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
<CheckableCommandItem
|
||||
value="all"
|
||||
:model-value="projectFilter === null"
|
||||
@update:model-value="$emit('update:project-filter', null)"
|
||||
>
|
||||
<span>All Projects</span>
|
||||
</CheckableCommandItem>
|
||||
<CheckableCommandItem
|
||||
v-for="project in projects"
|
||||
:key="project.id"
|
||||
:value="project.id.toString()"
|
||||
:model-value="projectFilter === project.id"
|
||||
@update:model-value="$emit('update:project-filter', project.id)"
|
||||
>
|
||||
<span>{{ project.name }}</span>
|
||||
</CheckableCommandItem>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
<!-- Episode Filter -->
|
||||
<Popover v-if="episodes.length > 0">
|
||||
<PopoverTrigger as-child>
|
||||
@@ -205,7 +248,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Search, ListFilter, Tag, Film, Package, User } from 'lucide-vue-next'
|
||||
import { Search, ListFilter, Tag, Film, Package, User, FolderOpen } from 'lucide-vue-next'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
@@ -229,6 +272,7 @@ import ClearFiltersButton from '@/components/shared/ClearFiltersButton.vue'
|
||||
import { useDebouncedSearch } from '@/composables/useDebouncedSearch'
|
||||
import type { VisibilityState } from '@tanstack/vue-table'
|
||||
import type { Episode } from '@/services/episode'
|
||||
import type { Project } from '@/services/project'
|
||||
|
||||
interface Props {
|
||||
statusFilter: string[]
|
||||
@@ -244,6 +288,10 @@ interface Props {
|
||||
myTasksFilter: boolean
|
||||
currentUserId: number | null
|
||||
isDetailPanelEnabled: boolean
|
||||
// Cross-project task lists (e.g. My Tasks) pass a project list + filter; the
|
||||
// project-scoped Tasks page omits these and the filter stays hidden.
|
||||
projects?: Project[]
|
||||
projectFilter?: number | null
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
@@ -257,6 +305,7 @@ const emit = defineEmits<{
|
||||
'update:search': [value: string]
|
||||
'update:column-visibility': [value: VisibilityState]
|
||||
'update:my-tasks-filter': [value: boolean]
|
||||
'update:project-filter': [value: number | null]
|
||||
'toggle-detail-panel': []
|
||||
}>()
|
||||
|
||||
@@ -298,7 +347,8 @@ const hasFilters = computed(() => {
|
||||
props.assigneeFilter.length > 0 ||
|
||||
props.contextFilter !== 'all' ||
|
||||
props.search !== '' ||
|
||||
props.myTasksFilter
|
||||
props.myTasksFilter ||
|
||||
(props.projectFilter ?? null) !== null
|
||||
)
|
||||
})
|
||||
|
||||
@@ -348,5 +398,8 @@ const clearFilters = () => {
|
||||
emit('update:context-filter', 'all')
|
||||
emit('update:search', '')
|
||||
emit('update:my-tasks-filter', false)
|
||||
if (props.projects) {
|
||||
emit('update:project-filter', null)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user