Offer department-owned task types in the shot/asset Add Task dropdowns
ShotDetailPanel and AssetDetailPanel now merge department task types (filtered to shot/asset departments respectively) into the flat task type list shown in "Add Task", so types like Animation's blocking or Composite's first_pass are selectable at creation time instead of only via editing a task's type afterward. No backend change needed since task creation already derives department from an owned task type.
This commit is contained in:
@@ -293,6 +293,7 @@ import { assetService, AssetStatus, type Asset, type TaskStatusInfo } from '@/se
|
|||||||
import { taskService, type ProductionNote, type Submission } from '@/services/task'
|
import { taskService, type ProductionNote, type Submission } from '@/services/task'
|
||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
|
import { useDepartmentsStore } from '@/stores/departments'
|
||||||
|
|
||||||
interface Task {
|
interface Task {
|
||||||
id: number
|
id: number
|
||||||
@@ -321,6 +322,7 @@ const emit = defineEmits<Emits>()
|
|||||||
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
const departmentsStore = useDepartmentsStore()
|
||||||
|
|
||||||
// Reactive state
|
// Reactive state
|
||||||
const asset = ref<Asset | null>(null)
|
const asset = ref<Asset | null>(null)
|
||||||
@@ -363,9 +365,15 @@ const progressPercentage = computed(() => {
|
|||||||
return Math.round((completedTasksCount.value / tasks.value.length) * 100)
|
return Math.round((completedTasksCount.value / tasks.value.length) * 100)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Task types offered in "Add Task" include the flat asset task type list
|
||||||
|
// plus every task type owned by an asset department (e.g. Modeling's own
|
||||||
|
// task types), deduped against types the asset already has a task for.
|
||||||
const availableTaskTypes = computed(() => {
|
const availableTaskTypes = computed(() => {
|
||||||
const existingTypes = new Set(tasks.value.map(task => task.task_type))
|
const existingTypes = new Set(tasks.value.map(task => task.task_type))
|
||||||
return props.allTaskTypes.filter(type => !existingTypes.has(type))
|
const departmentTaskTypes = departmentsStore.getDepartmentsByType(props.projectId, 'asset')
|
||||||
|
.flatMap(d => d.task_types)
|
||||||
|
const merged = Array.from(new Set([...props.allTaskTypes, ...departmentTaskTypes]))
|
||||||
|
return merged.filter(type => !existingTypes.has(type))
|
||||||
})
|
})
|
||||||
|
|
||||||
const taskStatusCounts = computed(() => {
|
const taskStatusCounts = computed(() => {
|
||||||
@@ -394,6 +402,7 @@ const loadAssetDetails = async () => {
|
|||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
error.value = null
|
error.value = null
|
||||||
asset.value = await assetService.getAsset(props.assetId)
|
asset.value = await assetService.getAsset(props.assetId)
|
||||||
|
departmentsStore.fetchProjectDepartments(props.projectId)
|
||||||
|
|
||||||
// Load users if not already loaded (for user name resolution)
|
// Load users if not already loaded (for user name resolution)
|
||||||
if (userStore.users.length === 0) {
|
if (userStore.users.length === 0) {
|
||||||
|
|||||||
@@ -341,6 +341,7 @@ import { shotService, type Shot, type TaskStatusInfo } from '@/services/shot'
|
|||||||
import { taskService, type ProductionNote, type Submission } from '@/services/task'
|
import { taskService, type ProductionNote, type Submission } from '@/services/task'
|
||||||
import { projectService, type ProjectMember } from '@/services/project'
|
import { projectService, type ProjectMember } from '@/services/project'
|
||||||
import { useTaskStatusesStore } from '@/stores/taskStatuses'
|
import { useTaskStatusesStore } from '@/stores/taskStatuses'
|
||||||
|
import { useDepartmentsStore } from '@/stores/departments'
|
||||||
import { useAvatarUrl } from '@/composables/useAvatarUrl'
|
import { useAvatarUrl } from '@/composables/useAvatarUrl'
|
||||||
import { usePermission } from '@/composables/usePermission'
|
import { usePermission } from '@/composables/usePermission'
|
||||||
|
|
||||||
@@ -375,6 +376,7 @@ const props = defineProps<Props>()
|
|||||||
const emit = defineEmits<Emits>()
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
const taskStatusesStore = useTaskStatusesStore()
|
const taskStatusesStore = useTaskStatusesStore()
|
||||||
|
const departmentsStore = useDepartmentsStore()
|
||||||
const { getAvatarUrl } = useAvatarUrl()
|
const { getAvatarUrl } = useAvatarUrl()
|
||||||
const { isAdmin, isCoordinatorOrAdmin } = usePermission()
|
const { isAdmin, isCoordinatorOrAdmin } = usePermission()
|
||||||
|
|
||||||
@@ -435,9 +437,15 @@ const canUploadReferences = computed(() => {
|
|||||||
|
|
||||||
const canEditDesign = computed(() => isCoordinatorOrAdmin.value)
|
const canEditDesign = computed(() => isCoordinatorOrAdmin.value)
|
||||||
|
|
||||||
|
// Task types offered in "Add Task" include the flat shot task type list
|
||||||
|
// plus every task type owned by a shot department (e.g. Animation's own
|
||||||
|
// task types), deduped against types the shot already has a task for.
|
||||||
const availableTaskTypes = computed(() => {
|
const availableTaskTypes = computed(() => {
|
||||||
const existingTypes = new Set(tasks.value.map(task => task.task_type))
|
const existingTypes = new Set(tasks.value.map(task => task.task_type))
|
||||||
return props.allTaskTypes.filter(type => !existingTypes.has(type))
|
const departmentTaskTypes = departmentsStore.getDepartmentsByType(props.projectId, 'shot')
|
||||||
|
.flatMap(d => d.task_types)
|
||||||
|
const merged = Array.from(new Set([...props.allTaskTypes, ...departmentTaskTypes]))
|
||||||
|
return merged.filter(type => !existingTypes.has(type))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
@@ -446,6 +454,7 @@ const loadShotDetails = async () => {
|
|||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
error.value = null
|
error.value = null
|
||||||
shot.value = props.initialShot ?? await shotService.getShot(props.shotId)
|
shot.value = props.initialShot ?? await shotService.getShot(props.shotId)
|
||||||
|
departmentsStore.fetchProjectDepartments(props.projectId)
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
taskStatusesStore.fetchProjectStatuses(props.projectId),
|
taskStatusesStore.fetchProjectStatuses(props.projectId),
|
||||||
loadProjectMembers()
|
loadProjectMembers()
|
||||||
|
|||||||
Reference in New Issue
Block a user