modify my task
This commit is contained in:
@@ -70,6 +70,23 @@ export const createColumns = (callbacks?: ColumnCallbacks): ColumnDef<Task>[] =>
|
||||
return h('div', { class: 'font-medium' }, row.getValue('name'))
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'project_name',
|
||||
header: ({ column }) => {
|
||||
return h(
|
||||
Button,
|
||||
{
|
||||
variant: 'ghost',
|
||||
onClick: () => column.toggleSorting(column.getIsSorted() === 'asc'),
|
||||
},
|
||||
() => ['Project', h(ArrowUpDown, { class: 'ml-2 h-4 w-4' })]
|
||||
)
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const projectName = row.getValue('project_name') as string | undefined
|
||||
return h('div', { class: 'text-sm' }, projectName || '-')
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'task_type',
|
||||
header: ({ column }) => {
|
||||
|
||||
@@ -143,6 +143,15 @@ class TaskService {
|
||||
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
|
||||
|
||||
@@ -1,89 +1,73 @@
|
||||
<template>
|
||||
<div class="h-full flex">
|
||||
<!-- Main Content -->
|
||||
<div :class="selectedTask ? 'flex-1' : 'w-full'" class="p-6 overflow-auto">
|
||||
<div class="max-w-7xl mx-auto space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold">My Tasks</h1>
|
||||
<p class="text-muted-foreground mt-1">
|
||||
Manage and track your assigned tasks
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="text-sm text-muted-foreground">
|
||||
<span class="font-semibold">{{ tasksStore.tasks.length }}</span> total tasks
|
||||
<span v-if="tasksStore.overdueTasks.length > 0" class="ml-4 text-destructive font-semibold">
|
||||
{{ tasksStore.overdueTasks.length }} overdue
|
||||
</span>
|
||||
</div>
|
||||
<div class="h-full flex flex-col">
|
||||
<!-- Header - Shot Task Style -->
|
||||
<div class="sticky top-0 z-10 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 border-b pb-4 px-4 sm:px-6 pt-4 sm:pt-6 mb-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 class="text-2xl font-semibold">My Tasks</h1>
|
||||
<p class="text-sm text-muted-foreground mt-1">
|
||||
View and track your assigned tasks across all projects
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="text-sm text-muted-foreground">
|
||||
<span class="font-semibold">{{ tasks.length }}</span> total tasks
|
||||
<span v-if="overdueTasks.length > 0" class="ml-4 text-destructive font-semibold">
|
||||
{{ overdueTasks.length }} overdue
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Task Statistics -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-5 gap-4">
|
||||
<Card>
|
||||
<CardHeader class="pb-2">
|
||||
<CardTitle class="text-sm font-medium text-muted-foreground">Not Started</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-2xl font-bold">
|
||||
{{ getTaskCountByStatus('not_started') }}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader class="pb-2">
|
||||
<CardTitle class="text-sm font-medium text-muted-foreground">In Progress</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-2xl font-bold text-blue-600">
|
||||
{{ getTaskCountByStatus('in_progress') }}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader class="pb-2">
|
||||
<CardTitle class="text-sm font-medium text-muted-foreground">Submitted</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-2xl font-bold text-purple-600">
|
||||
{{ getTaskCountByStatus('submitted') }}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader class="pb-2">
|
||||
<CardTitle class="text-sm font-medium text-muted-foreground">Approved</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-2xl font-bold text-green-600">
|
||||
{{ getTaskCountByStatus('approved') }}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader class="pb-2">
|
||||
<CardTitle class="text-sm font-medium text-muted-foreground">Retake</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-2xl font-bold text-destructive">
|
||||
{{ getTaskCountByStatus('retake') }}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<!-- Task List -->
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Task List</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<TaskList @task-selected="handleTaskSelected" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<!-- Filter Toolbar -->
|
||||
<div class="flex flex-wrap gap-2 mt-4">
|
||||
<!-- Project Filter -->
|
||||
<Select v-model="selectedProjectId" @update:model-value="handleProjectChange">
|
||||
<SelectTrigger class="w-[250px] h-8">
|
||||
<SelectValue placeholder="All Projects" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">All Projects</SelectItem>
|
||||
<SelectItem v-for="project in projects" :key="project.id" :value="String(project.id)">
|
||||
{{ project.name }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<!-- Status Filter -->
|
||||
<Select v-model="selectedStatus" @update:model-value="handleStatusChange">
|
||||
<SelectTrigger class="w-[200px] h-8">
|
||||
<SelectValue placeholder="All Statuses" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">All Statuses</SelectItem>
|
||||
<SelectItem value="not_started">Not Started</SelectItem>
|
||||
<SelectItem value="in_progress">In Progress</SelectItem>
|
||||
<SelectItem value="submitted">Submitted</SelectItem>
|
||||
<SelectItem value="approved">Approved</SelectItem>
|
||||
<SelectItem value="retake">Retake</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="flex-1 overflow-auto px-4 sm:px-6 pb-6">
|
||||
<!-- Task List -->
|
||||
<div v-if="isLoading" class="flex items-center justify-center py-12">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="animate-spin rounded-full h-6 w-6 border-b-2 border-primary"></div>
|
||||
<span class="text-muted-foreground">Loading tasks...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="tasks.length === 0" class="text-center py-12">
|
||||
<ListTodo class="h-12 w-12 mx-auto text-muted-foreground mb-4" />
|
||||
<h3 class="text-lg font-semibold mb-2">No tasks found</h3>
|
||||
<p class="text-muted-foreground">
|
||||
{{ selectedProjectId !== 'all' || selectedStatus !== 'all' ? 'Try adjusting your filters' : 'No tasks assigned to you' }}
|
||||
</p>
|
||||
</div>
|
||||
<TaskList v-else @task-selected="handleTaskSelected" :tasks="tasks" />
|
||||
</div>
|
||||
|
||||
<!-- Task Detail Panel -->
|
||||
@@ -97,18 +81,63 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useTasksStore } from '@/stores/tasks'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ListTodo } from 'lucide-vue-next'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import TaskList from '@/components/task/TaskList.vue'
|
||||
import TaskDetailPanel from '@/components/task/TaskDetailPanel.vue'
|
||||
import type { TaskListItem } from '@/services/task'
|
||||
import { taskService, type TaskListItem } from '@/services/task'
|
||||
import { projectService, type Project } from '@/services/project'
|
||||
|
||||
const tasksStore = useTasksStore()
|
||||
const router = useRouter()
|
||||
|
||||
// State
|
||||
const tasks = ref<TaskListItem[]>([])
|
||||
const projects = ref<Project[]>([])
|
||||
const selectedProjectId = ref<string>('all')
|
||||
const selectedStatus = ref<string>('all')
|
||||
const isLoading = ref(false)
|
||||
const selectedTask = ref<TaskListItem | null>(null)
|
||||
|
||||
function getTaskCountByStatus(status: string): number {
|
||||
return tasksStore.tasks.filter((task: any) => task.status === status).length
|
||||
// Computed
|
||||
const overdueTasks = computed(() => {
|
||||
const now = new Date()
|
||||
return tasks.value.filter(task => {
|
||||
if (!task.deadline) return false
|
||||
const deadline = new Date(task.deadline)
|
||||
return deadline < now && task.status !== 'approved'
|
||||
})
|
||||
})
|
||||
|
||||
// Methods
|
||||
async function fetchMyTasks() {
|
||||
isLoading.value = true
|
||||
try {
|
||||
const projectId = selectedProjectId.value === 'all' ? undefined : parseInt(selectedProjectId.value)
|
||||
const status = selectedStatus.value === 'all' ? undefined : selectedStatus.value
|
||||
tasks.value = await taskService.getMyTasks(projectId, status)
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch my tasks:', error)
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchProjects() {
|
||||
try {
|
||||
projects.value = await projectService.getUserProjects()
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch projects:', error)
|
||||
}
|
||||
}
|
||||
|
||||
function handleProjectChange() {
|
||||
fetchMyTasks()
|
||||
}
|
||||
|
||||
function handleStatusChange() {
|
||||
fetchMyTasks()
|
||||
}
|
||||
|
||||
function handleTaskSelected(task: any) {
|
||||
@@ -120,7 +149,11 @@ function handleClosePanel() {
|
||||
}
|
||||
|
||||
function handleTaskUpdated() {
|
||||
// Refresh tasks
|
||||
tasksStore.fetchTasks()
|
||||
fetchMyTasks()
|
||||
}
|
||||
|
||||
// Lifecycle
|
||||
onMounted(async () => {
|
||||
await Promise.all([fetchMyTasks(), fetchProjects()])
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user