Generalize aggregated notes to a shared component, reuse in Asset panel
Moves shot/ShotNotes.vue to shared/EntityNotes.vue (it had no shot-specific logic) and wires it into AssetDetailPanel.vue too, replacing the old read-only AssetNotes.vue. Asset panel gains the same multi-select task filter, sort, client-only/submission-notes toggles, and bottom composer that Shot's panel already had, plus the same min-h-0 layout fix needed for the pinned composer. NoteItem's absolute-format timestamp now shows date-only (y/m/d) with an info icon whose tooltip reveals the full y/m/d H:M:S - applied to both note and submission-note entries. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,8 +10,8 @@
|
||||
/>
|
||||
|
||||
<!-- Asset Details -->
|
||||
<div v-else-if="asset" class="flex-1 overflow-y-auto">
|
||||
<DetailPanelHeader :title="asset.name" :deleted-at="asset.deleted_at" @close="$emit('close')">
|
||||
<div v-else-if="asset" class="flex-1 flex flex-col min-h-0">
|
||||
<DetailPanelHeader class="flex-shrink-0" :title="asset.name" :deleted-at="asset.deleted_at" @close="$emit('close')">
|
||||
<template #badges>
|
||||
<!-- Deletion status indicator for admins -->
|
||||
<Badge v-if="authStore.isAdmin && asset.deleted_at" variant="destructive" class="text-xs flex-shrink-0">
|
||||
@@ -21,8 +21,8 @@
|
||||
</DetailPanelHeader>
|
||||
|
||||
<!-- Tabbed Content -->
|
||||
<Tabs default-value="infos" class="flex-1 flex flex-col">
|
||||
<TabsList class="mx-0 mt-0 grid w-full grid-cols-3 rounded-none border-b">
|
||||
<Tabs default-value="infos" class="flex-1 flex flex-col min-h-0">
|
||||
<TabsList class="flex-shrink-0 mx-0 mt-0 grid w-full grid-cols-3 rounded-none border-b">
|
||||
<TabsTrigger value="infos" title="Infos">
|
||||
<Info class="h-4 w-4" />
|
||||
<span class="sr-only">Infos</span>
|
||||
@@ -255,7 +255,13 @@
|
||||
|
||||
<!-- Notes Tab -->
|
||||
<TabsContent value="notes" class="flex-1 m-0 overflow-hidden">
|
||||
<AssetNotes :asset-id="assetId" :notes="notes" @notes-updated="loadNotes" />
|
||||
<EntityNotes
|
||||
:key="assetId"
|
||||
:tasks="tasks"
|
||||
:notes="notes"
|
||||
:submissions="submissions"
|
||||
@notes-updated="loadNotes"
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<!-- References Tab -->
|
||||
@@ -280,11 +286,11 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover
|
||||
import DetailPanelLoading from '@/components/shared/DetailPanelLoading.vue'
|
||||
import DetailPanelError from '@/components/shared/DetailPanelError.vue'
|
||||
import DetailPanelHeader from '@/components/shared/DetailPanelHeader.vue'
|
||||
import AssetNotes from './AssetNotes.vue'
|
||||
import EntityNotes from '@/components/shared/EntityNotes.vue'
|
||||
import AssetReferences from './AssetReferences.vue'
|
||||
|
||||
import { assetService, AssetStatus, type Asset, type TaskStatusInfo } from '@/services/asset'
|
||||
import { taskService } from '@/services/task'
|
||||
import { taskService, type ProductionNote, type Submission } from '@/services/task'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
@@ -318,7 +324,8 @@ const userStore = useUserStore()
|
||||
|
||||
// Reactive state
|
||||
const asset = ref<Asset | null>(null)
|
||||
const notes = ref<any[]>([])
|
||||
const notes = ref<ProductionNote[]>([])
|
||||
const submissions = ref<Submission[]>([])
|
||||
const references = ref<any[]>([])
|
||||
const isLoading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
@@ -405,28 +412,18 @@ const loadAssetDetails = async () => {
|
||||
}
|
||||
|
||||
const loadNotes = async () => {
|
||||
if (tasks.value.length === 0) {
|
||||
notes.value = []
|
||||
submissions.value = []
|
||||
return
|
||||
}
|
||||
try {
|
||||
// Load notes from all tasks associated with this asset
|
||||
const { taskService } = await import('@/services/task')
|
||||
const allNotes: any[] = []
|
||||
|
||||
for (const task of tasks.value) {
|
||||
if (task.id) {
|
||||
const taskNotes = await taskService.getTaskNotes(task.id)
|
||||
// Add task info to each note for context
|
||||
const notesWithContext = taskNotes.map(note => ({
|
||||
...note,
|
||||
task_name: task.name,
|
||||
task_type: task.task_type
|
||||
}))
|
||||
allNotes.push(...notesWithContext)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by date (newest first)
|
||||
notes.value = allNotes.sort((a, b) =>
|
||||
new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
|
||||
)
|
||||
const [notesByTask, submissionsByTask] = await Promise.all([
|
||||
Promise.all(tasks.value.map(task => taskService.getTaskNotes(task.id).catch(() => []))),
|
||||
Promise.all(tasks.value.map(task => taskService.getTaskSubmissions(task.id).catch(() => [])))
|
||||
])
|
||||
notes.value = notesByTask.flat()
|
||||
submissions.value = submissionsByTask.flat()
|
||||
} catch (err) {
|
||||
console.error('Failed to load notes:', err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user