Files
LinkDesk/frontend/src/services/task.ts
T
indigo c09710f4e5 Add per-project Department management
Departments are now a customizable per-project list (standard + custom),
matching the existing custom task type/status pattern, instead of a fixed
7-value enum. They're usable directly on tasks (new field, independent of
assignee) and continue to drive team member department roles.
2026-07-22 04:03:33 +08:00

316 lines
8.7 KiB
TypeScript

import { apiClient } from './api'
import { TaskStatus } from './asset'
export interface TaskStatusUpdate {
status: TaskStatus
}
export interface Task {
id: number
name: string
description?: string
task_type: string
status: TaskStatus
department?: string
start_date?: string
deadline?: string
project_id: number
project_name?: string
episode_id?: number
episode_name?: string
shot_id?: number
shot_name?: string
asset_id?: number
asset_name?: string
assigned_user_id?: number
assigned_user_name?: string
assigned_user_email?: string
assigned_user_avatar_url?: string | null
created_at: string
updated_at: string
}
export interface TaskListItem {
id: number
name: string
task_type: string
status: TaskStatus
department?: string
start_date?: string
deadline?: string
project_id: number
project_name: string
episode_id?: number
episode_name?: string
shot_id?: number
shot_name?: string
asset_id?: number
asset_name?: string
assigned_user_id?: number
assigned_user_name?: string
created_at: string
updated_at: string
}
export interface TaskStatusInfo {
task_type: string
status: TaskStatus
task_id?: number
assigned_user_id?: number
}
export type NoteType = 'internal' | 'client'
export interface ProductionNote {
id: number
content: string
note_type: NoteType
parent_note_id?: number
task_id: number
user_id: number
created_at: string
updated_at: string
user_first_name: string
user_last_name: string
user_email: string
user_avatar_url?: string | null
child_notes?: ProductionNote[]
}
export interface TaskAttachment {
id: number
task_id: number
user_id: number
file_name: string
file_path: string
file_type: string
file_size: number
attachment_type: string
description?: string
uploaded_at: string
user_first_name: string
user_last_name: string
download_url?: string
thumbnail_url?: string
}
export interface Submission {
id: number
task_id: number
user_id: number
file_path: string
file_name: string
version_number: number
notes?: string
submitted_at: string
user_first_name: string
user_last_name: string
latest_review?: any
download_url?: string
thumbnail_url?: string
stream_url?: string
}
export interface SubmissionDateInfo {
task_id: number
submitted_at: string
}
export interface TaskFilters {
projectId?: number
shotId?: number
assetId?: number
assignedUserId?: number
status?: string
taskType?: string
departmentRole?: string
limit?: number
}
export interface BulkStatusUpdateRequest {
task_ids: number[]
status: string
}
export interface BulkAssignmentRequest {
task_ids: number[]
assigned_user_id: number
}
export interface BulkActionResult {
success_count: number
failed_count: number
errors?: Array<{ task_id: number; error: string }>
}
class TaskService {
async getTasks(filters?: TaskFilters): Promise<TaskListItem[]> {
const params = new URLSearchParams()
if (filters?.projectId) params.append('project_id', filters.projectId.toString())
if (filters?.shotId) params.append('shot_id', filters.shotId.toString())
if (filters?.assetId) params.append('asset_id', filters.assetId.toString())
if (filters?.assignedUserId) params.append('assigned_user_id', filters.assignedUserId.toString())
if (filters?.status) params.append('status', filters.status)
if (filters?.taskType) params.append('task_type', filters.taskType)
if (filters?.departmentRole) params.append('department_role', filters.departmentRole)
if (filters?.limit) params.append('limit', filters.limit.toString())
const response = await apiClient.get(`/tasks/?${params}`)
return response.data
}
async getMyTasks(projectId?: number, status?: string): Promise<TaskListItem[]> {
const params = new URLSearchParams()
if (projectId) params.append('project_id', projectId.toString())
if (status) params.append('status', status)
const response = await apiClient.get(`/tasks/my-tasks?${params}`)
return response.data
}
async getTask(taskId: number): Promise<Task> {
const response = await apiClient.get(`/tasks/${taskId}`)
return response.data
}
async updateTaskStatus(taskId: number, status: TaskStatus): Promise<Task> {
const response = await apiClient.put(`/tasks/${taskId}/status`, {
status
})
return response.data
}
async updateTask(taskId: number, updates: Partial<Task>): Promise<Task> {
const response = await apiClient.put(`/tasks/${taskId}`, updates)
return response.data
}
async assignTask(taskId: number, assignedUserId: number): Promise<Task> {
const response = await apiClient.put(`/tasks/${taskId}/assign`, {
assigned_user_id: assignedUserId
})
return response.data
}
// Notes
async getTaskNotes(taskId: number): Promise<ProductionNote[]> {
const response = await apiClient.get(`/tasks/${taskId}/notes`)
return response.data
}
async createTaskNote(taskId: number, content: string, parentNoteId?: number, noteType: NoteType = 'internal'): Promise<ProductionNote> {
const response = await apiClient.post(`/tasks/${taskId}/notes`, {
content,
parent_note_id: parentNoteId,
note_type: noteType
})
return response.data
}
async updateTaskNote(taskId: number, noteId: number, content: string): Promise<ProductionNote> {
const response = await apiClient.put(`/tasks/${taskId}/notes/${noteId}`, {
content
})
return response.data
}
async deleteTaskNote(taskId: number, noteId: number): Promise<void> {
await apiClient.delete(`/tasks/${taskId}/notes/${noteId}`)
}
// Attachments
async getTaskAttachments(taskId: number, attachmentType?: string): Promise<TaskAttachment[]> {
const params = attachmentType ? `?attachment_type=${attachmentType}` : ''
const response = await apiClient.get(`/tasks/${taskId}/attachments${params}`)
return response.data
}
async uploadTaskAttachment(
taskId: number,
file: File,
attachmentType: string = 'reference',
description?: string
): Promise<TaskAttachment> {
const formData = new FormData()
formData.append('file', file)
formData.append('attachment_type', attachmentType)
if (description) {
formData.append('description', description)
}
const response = await apiClient.post(`/tasks/${taskId}/attachments`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
return response.data
}
async deleteTaskAttachment(taskId: number, attachmentId: number): Promise<void> {
await apiClient.delete(`/tasks/${taskId}/attachments/${attachmentId}`)
}
// Submissions
async getTaskSubmissions(taskId: number): Promise<Submission[]> {
const response = await apiClient.get(`/tasks/${taskId}/submissions`)
return response.data
}
async getSubmissionDates(projectId: number): Promise<SubmissionDateInfo[]> {
const response = await apiClient.get(`/tasks/submission-dates?project_id=${projectId}`)
return response.data
}
async submitWork(taskId: number, file: File, notes?: string): Promise<Submission> {
const formData = new FormData()
formData.append('file', file)
if (notes) {
formData.append('notes', notes)
}
const response = await apiClient.post(`/tasks/${taskId}/submit`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
return response.data
}
async updateSubmission(taskId: number, submissionId: number, notes: string): Promise<Submission> {
const response = await apiClient.put(`/tasks/${taskId}/submissions/${submissionId}`, { notes })
return response.data
}
async deleteSubmission(taskId: number, submissionId: number): Promise<void> {
await apiClient.delete(`/tasks/${taskId}/submissions/${submissionId}`)
}
async createAssetTask(assetId: number, taskType: string): Promise<TaskStatusInfo> {
const response = await apiClient.post(`/assets/${assetId}/tasks?task_type=${taskType}`)
return response.data
}
async createShotTask(shotId: number, taskType: string): Promise<TaskStatusInfo> {
const response = await apiClient.post(`/shots/${shotId}/tasks?task_type=${taskType}`)
return response.data
}
// Bulk Actions
async bulkUpdateStatus(taskIds: number[], status: string): Promise<BulkActionResult> {
const response = await apiClient.put('/tasks/bulk/status', {
task_ids: taskIds,
status
})
return response.data
}
async bulkAssignTasks(taskIds: number[], assignedUserId: number): Promise<BulkActionResult> {
const response = await apiClient.put('/tasks/bulk/assign', {
task_ids: taskIds,
assigned_user_id: assignedUserId
})
return response.data
}
}
export const taskService = new TaskService()