Phase 3: Unify controls across shot/asset/task toolbars and detail panels

Extracts duplication that built up as shot/asset/task features reached
parity: CheckableCommandItem/ColumnToggleList replace 14+ hand-rolled
checkbox-list blocks, shared toolbar pieces (debounced search, detail-panel
toggle, clear-filters, segmented view/context toggle) replace copy-pasted
markup in the three table toolbars, icon-only buttons standardize on the
icon-sm size, TaskBulkActionsMenu's Assign To submenu matches Set Status,
and a shared DetailPanelOverlay/Header/Loading/Error shell backs all three
detail panels (adding a previously-missing error state to the task panel).
This commit is contained in:
2026-07-18 02:15:55 +08:00
parent cd3628a255
commit 26807984ee
34 changed files with 714 additions and 1146 deletions
+25 -58
View File
@@ -173,63 +173,30 @@
@confirm-delete="handleDeleteAsset"
/>
</div>
<!-- Asset Detail Panel (Desktop) with slide animation -->
<Transition
enter-active-class="transition-transform duration-300 ease-out"
enter-from-class="translate-x-full"
enter-to-class="translate-x-0"
leave-active-class="transition-transform duration-300 ease-in"
leave-from-class="translate-x-0"
leave-to-class="translate-x-full"
>
<div
v-if="showPanel && selectedAsset"
class="fixed right-0 top-[113px] bottom-0 w-96 bg-background border-l shadow-lg z-50 hidden lg:block overflow-y-auto"
>
<TaskDetailPanel
v-if="selectedTaskId"
:key="selectedTaskId"
:task-id="selectedTaskId"
:initial-tab="selectedTaskTab"
@close="selectedTaskId = null"
@task-updated="loadAssets"
/>
<AssetDetailPanel
v-else
:project-id="projectId"
:asset-id="selectedAsset.id"
:all-task-types="allTaskTypes"
@close="closeDetailPanel"
@edit="editAsset"
@delete="deleteAsset"
@select-task="handleSelectTask"
/>
</div>
</Transition>
<!-- Asset Detail Panel (Mobile) -->
<Sheet v-model:open="showMobileDetail">
<SheetContent side="right" class="w-full sm:max-w-md p-0">
<TaskDetailPanel
v-if="selectedTaskId"
:key="selectedTaskId"
:task-id="selectedTaskId"
:initial-tab="selectedTaskTab"
@close="selectedTaskId = null"
@task-updated="loadAssets"
/>
<AssetDetailPanel
v-else-if="selectedAsset"
:project-id="projectId"
:asset-id="selectedAsset.id"
:all-task-types="allTaskTypes"
@close="closeDetailPanel"
@edit="editAsset"
@delete="deleteAsset"
@select-task="handleSelectTask"
/>
</SheetContent>
</Sheet>
<!-- Asset Detail Panel (Desktop + Mobile) -->
<DetailPanelOverlay
:visible="!!(showPanel && selectedAsset)"
v-model:mobile-open="showMobileDetail"
>
<TaskDetailPanel
v-if="selectedTaskId"
:key="selectedTaskId"
:task-id="selectedTaskId"
:initial-tab="selectedTaskTab"
@close="selectedTaskId = null"
@task-updated="loadAssets"
/>
<AssetDetailPanel
v-else-if="selectedAsset"
:project-id="projectId"
:asset-id="selectedAsset.id"
:all-task-types="allTaskTypes"
@close="closeDetailPanel"
@edit="editAsset"
@delete="deleteAsset"
@select-task="handleSelectTask"
/>
</DetailPanelOverlay>
</div>
</template>
@@ -253,7 +220,7 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Sheet, SheetContent } from "@/components/ui/sheet";
import DetailPanelOverlay from "@/components/shared/DetailPanelOverlay.vue";
import AssetCard from "./AssetCard.vue";
import AssetForm from "./AssetForm.vue";
import AssetDetailPanel from "./AssetDetailPanel.vue";