Redesign note display as message cards; fix reply and Textarea v-model bugs

- NoteItem.vue: render each note as a rounded card (own notes tinted)
  instead of a flat list row.
- TaskNotes.vue: show a "Replying to X" banner with cancel and
  auto-focus the composer when Reply is clicked, so replying is
  actually visible instead of silently setting hidden state.
- ShotDetailPanel/ShotBrowser/TaskDetailPanel: carry the target note
  id through select-task so replying from the shot-level aggregated
  notes view pre-fills the reply in the destination task's panel
  instead of just switching tabs.
- Textarea.vue: fix v-model, which was never wired up (modelValue/
  update:modelValue fell through as dead attrs on the native
  textarea), so typed content never reached the bound ref anywhere
  it's used - notes, submissions, and shot/asset/episode/project
  description fields.
- AssetDetailPanel/ShotDetailPanel/TaskDetailPanel: drop the header
  status badge and switch tab labels to icons (with an unread-style
  badge on Notes), fixing the TabsTrigger clipping/vertical alignment
  it introduced.
This commit is contained in:
2026-07-19 00:00:11 +08:00
parent 4bffec642e
commit 762bd34f74
8 changed files with 262 additions and 133 deletions
@@ -13,13 +13,6 @@
<div v-else-if="asset" class="flex-1 overflow-y-auto">
<DetailPanelHeader :title="asset.name" :deleted-at="asset.deleted_at" @close="$emit('close')">
<template #badges>
<Badge :variant="getStatusVariant(asset.status)" class="text-xs flex-shrink-0">
<div
class="w-2 h-2 rounded-full mr-1"
:class="getStatusColor(asset.status)"
></div>
{{ formatStatus(asset.status) }}
</Badge>
<!-- Deletion status indicator for admins -->
<Badge v-if="authStore.isAdmin && asset.deleted_at" variant="destructive" class="text-xs flex-shrink-0">
Deleted {{ formatDeletedDate(asset.deleted_at) }}
@@ -30,16 +23,26 @@
<!-- 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">
<TabsTrigger value="infos">Infos</TabsTrigger>
<TabsTrigger value="notes">
Notes
<Badge v-if="notes.length > 0" variant="secondary" class="ml-2">
{{ notes.length }}
</Badge>
<TabsTrigger value="infos" title="Infos">
<Info class="h-4 w-4" />
<span class="sr-only">Infos</span>
</TabsTrigger>
<TabsTrigger value="references">
References
<Badge v-if="references.length > 0" variant="secondary" class="ml-2">
<TabsTrigger value="notes" title="Notes">
<span class="relative inline-flex">
<MessageSquare class="h-4 w-4" />
<span
v-if="notes.length > 0"
class="absolute -top-1.5 -right-1.5 h-3.5 w-3.5 rounded-full bg-red-500 text-white text-[9px] leading-none flex items-center justify-center"
>
{{ notes.length > 99 ? '99+' : notes.length }}
</span>
</span>
<span class="sr-only">Notes</span>
</TabsTrigger>
<TabsTrigger value="references" title="References">
<Image class="h-4 w-4" />
<span class="sr-only">References</span>
<Badge v-if="references.length > 0" variant="secondary" class="ml-1 h-4 px-1 text-[10px]">
{{ references.length }}
</Badge>
</TabsTrigger>
@@ -267,7 +270,7 @@
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import {
Plus, MessageSquarePlus, Paperclip, Send
Plus, MessageSquarePlus, Paperclip, Send, Info, MessageSquare, Image
} from 'lucide-vue-next'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'