Phase 2: asset/shot feature parity, task actions, member management
Phase 2 of frontend_tasks.md - close feature gaps between domains: - Asset user-assignment popover, ported from shot's EditableTaskStatus, wired through columns.ts and AssetBrowser.vue. - Asset column-locking toggle: two-pane frozen-column layout ported into AssetsDataTable.vue (adapted for asset's page-scroll layout, which has no bounded-height container like shot's). - Task table row-actions menu (View Details + Reassign only - no Delete/Edit, since no backend support exists for either). - Real select-task/create-task behavior on asset and shot detail panels: clicking a task swaps in the actual TaskDetailPanel in place; "Add Task" opens a task-type picker that creates real tasks via the existing createAssetTask/createShotTask services. - create-note/upload-reference/publish-version implemented via a task-picker that deep-links into TaskDetailPanel's Notes/ Attachments/Submissions tabs (new initialTab prop), reusing the already-working task-level components instead of building three new bespoke forms. Also fixed shot's pre-existing dead "Add Note"/ "Upload Reference" buttons the same way. - Consolidated ProjectMembersManager.vue and ProjectMemberManagement.vue into one component, combining remove-confirmation and approved-user filtering with toast feedback and the shared Select/Dialog UI kit. - Wired ProjectDetailView's "Manage Members" to navigate to the project's Settings > Team tab. Also fixed a bug in this session's own new code: TaskBrowser.vue's row-click handler is a no-op by design, so the new row-actions menu needed to emit row-double-click (which actually opens the panel) instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { h, ref } from 'vue'
|
||||
import type { ColumnDef } from '@tanstack/vue-table'
|
||||
import { ArrowUpDown, Film, Package, ChevronDown } from 'lucide-vue-next'
|
||||
import { ArrowUpDown, Film, Package, ChevronDown, MoreHorizontal, Eye, UserCog } from 'lucide-vue-next'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
@@ -9,6 +9,12 @@ import {
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/components/ui/popover'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import TaskStatusBadge from '@/components/asset/TaskStatusBadge.vue'
|
||||
import EditableTaskStatus from '@/components/task/EditableTaskStatus.vue'
|
||||
import { type Task } from '@/services/task'
|
||||
@@ -27,6 +33,8 @@ interface ColumnCallbacks {
|
||||
onBulkStatusChange?: (status: TaskStatus) => void
|
||||
onStatusUpdated?: (taskId: number, newStatus: TaskStatus) => void
|
||||
getSelectedCount?: () => number
|
||||
onViewDetails?: (task: Task) => void
|
||||
onReassign?: (task: Task) => void
|
||||
}
|
||||
|
||||
export const createColumns = (callbacks?: ColumnCallbacks): ColumnDef<Task>[] => {
|
||||
@@ -308,5 +316,84 @@ export const createColumns = (callbacks?: ColumnCallbacks): ColumnDef<Task>[] =>
|
||||
return h('div', { class: 'text-sm text-muted-foreground' }, formatDate(row.getValue('created_at')))
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
cell: ({ row }) => {
|
||||
const task = row.original
|
||||
return h(
|
||||
DropdownMenu,
|
||||
{},
|
||||
{
|
||||
default: () => [
|
||||
h(
|
||||
DropdownMenuTrigger,
|
||||
{ asChild: true },
|
||||
{
|
||||
default: () =>
|
||||
h(
|
||||
Button,
|
||||
{
|
||||
variant: 'ghost',
|
||||
size: 'sm',
|
||||
class: 'h-8 w-8 p-0',
|
||||
onMouseDown: (e: Event) => {
|
||||
e.stopPropagation()
|
||||
},
|
||||
onClick: (e: Event) => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () => h(MoreHorizontal, { class: 'h-4 w-4' }),
|
||||
}
|
||||
),
|
||||
}
|
||||
),
|
||||
h(
|
||||
DropdownMenuContent,
|
||||
{ align: 'end' },
|
||||
{
|
||||
default: () => [
|
||||
h(
|
||||
DropdownMenuItem,
|
||||
{
|
||||
onClick: (e: Event) => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
callbacks?.onViewDetails?.(task)
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () => [
|
||||
h(Eye, { class: 'h-4 w-4 mr-2' }),
|
||||
'View Details',
|
||||
],
|
||||
}
|
||||
),
|
||||
h(
|
||||
DropdownMenuItem,
|
||||
{
|
||||
onClick: (e: Event) => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
callbacks?.onReassign?.(task)
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () => [
|
||||
h(UserCog, { class: 'h-4 w-4 mr-2' }),
|
||||
'Reassign',
|
||||
],
|
||||
}
|
||||
),
|
||||
],
|
||||
}
|
||||
),
|
||||
],
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user