round 4
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount, nextTick, computed } from 'vue'
|
||||
import { ref, watch, onBeforeUnmount, nextTick, computed } from 'vue'
|
||||
import { defineProps, defineEmits } from 'vue'
|
||||
import { getCachedImageUrl, revokeAllImageUrls } from '../../common/imageCache'
|
||||
import type { Task } from '@/common/models'
|
||||
@@ -14,7 +14,6 @@ const props = defineProps<{
|
||||
filterType?: number | null
|
||||
}>()
|
||||
const emit = defineEmits<{
|
||||
(e: 'points-updated', payload: { id: string; points: number }): void
|
||||
(e: 'trigger-task', task: Task): void
|
||||
}>()
|
||||
|
||||
@@ -119,7 +118,18 @@ const filteredTasks = computed(() => {
|
||||
return tasks.value
|
||||
})
|
||||
|
||||
onMounted(fetchTasks)
|
||||
watch(
|
||||
() => props.taskIds,
|
||||
(newTaskIds) => {
|
||||
if (newTaskIds && newTaskIds.length > 0) {
|
||||
fetchTasks()
|
||||
} else {
|
||||
tasks.value = []
|
||||
loading.value = false
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
// revoke all created object URLs when component unmounts
|
||||
onBeforeUnmount(() => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted, computed } from 'vue'
|
||||
import { getCachedImageUrl } from '../../common/imageCache'
|
||||
import type { Task } from '@/common/models'
|
||||
|
||||
const props = defineProps<{
|
||||
childId?: string | number
|
||||
@@ -9,7 +10,7 @@ const props = defineProps<{
|
||||
deletable?: boolean
|
||||
selectable?: boolean
|
||||
}>()
|
||||
const emit = defineEmits(['edit-task', 'delete-task'])
|
||||
const emit = defineEmits(['edit-task', 'delete-task', 'loading-complete'])
|
||||
|
||||
const tasks = ref<
|
||||
{
|
||||
@@ -36,12 +37,15 @@ const fetchTasks = async () => {
|
||||
const resp = await fetch(url)
|
||||
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
|
||||
const data = await resp.json()
|
||||
const assigned = (data.assigned_tasks || []).map((task: any) => ({ ...task, assigned: true }))
|
||||
const assignable = (data.assignable_tasks || []).map((task: any) => ({
|
||||
const assigned = (data.assigned_tasks || []).map((task: Task) => ({
|
||||
...task,
|
||||
assigned: true,
|
||||
}))
|
||||
const assignable = (data.assignable_tasks || []).map((task: Task) => ({
|
||||
...task,
|
||||
assigned: false,
|
||||
}))
|
||||
let taskList: any[] = []
|
||||
let taskList: Task[] = []
|
||||
if (props.assignFilter === 'assignable') {
|
||||
taskList = assignable
|
||||
} else if (props.assignFilter === 'assigned') {
|
||||
@@ -53,7 +57,7 @@ const fetchTasks = async () => {
|
||||
}
|
||||
// Fetch images for each task if image_id is present
|
||||
await Promise.all(
|
||||
taskList.map(async (task: any) => {
|
||||
taskList.map(async (task: Task) => {
|
||||
if (task.image_id) {
|
||||
try {
|
||||
task.image_url = await getCachedImageUrl(task.image_id)
|
||||
@@ -67,7 +71,7 @@ const fetchTasks = async () => {
|
||||
|
||||
// If selectable, pre-select assigned tasks
|
||||
if (props.selectable) {
|
||||
selectedTasks.value = assigned.map((task: any) => String(task.id))
|
||||
selectedTasks.value = assigned.map((task: Task) => String(task.id))
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : 'Failed to fetch tasks'
|
||||
@@ -84,7 +88,7 @@ const fetchTasks = async () => {
|
||||
const data = await resp.json()
|
||||
const taskList = data.tasks || []
|
||||
await Promise.all(
|
||||
taskList.map(async (task: any) => {
|
||||
taskList.map(async (task: Task) => {
|
||||
if (task.image_id) {
|
||||
try {
|
||||
task.image_url = await getCachedImageUrl(task.image_id)
|
||||
@@ -101,6 +105,7 @@ const fetchTasks = async () => {
|
||||
tasks.value = []
|
||||
if (props.selectable) selectedTasks.value = []
|
||||
} finally {
|
||||
emit('loading-complete', tasks.value.length)
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
<template>
|
||||
<div class="task-view">
|
||||
<div v-if="taskCountRef === 0" class="no-tasks-message">
|
||||
<div>No Tasks</div>
|
||||
<div class="sub-message">
|
||||
<button class="create-btn" @click="createTask">Create</button> a task
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TaskList
|
||||
v-else
|
||||
ref="taskListRef"
|
||||
:deletable="true"
|
||||
@edit-task="(taskId) => $router.push({ name: 'EditTask', params: { id: taskId } })"
|
||||
@delete-task="confirmDeleteTask"
|
||||
@loading-complete="(count) => (taskCountRef = count)"
|
||||
/>
|
||||
|
||||
<!-- Floating Action Button -->
|
||||
@@ -37,6 +46,7 @@ const $router = useRouter()
|
||||
const showConfirm = ref(false)
|
||||
const taskToDelete = ref<string | null>(null)
|
||||
const taskListRef = ref()
|
||||
const taskCountRef = ref<number>(0)
|
||||
|
||||
function confirmDeleteTask(taskId: string) {
|
||||
taskToDelete.value = taskId
|
||||
@@ -145,4 +155,37 @@ const createTask = () => {
|
||||
.fab:hover {
|
||||
background: #5a67d8;
|
||||
}
|
||||
|
||||
.no-tasks-message {
|
||||
margin: 2rem 0;
|
||||
font-size: 1.15rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
color: #fdfdfd;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.sub-message {
|
||||
margin-top: 0.3rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
color: #b5ccff;
|
||||
}
|
||||
.create-btn {
|
||||
background: #fff;
|
||||
color: #2563eb;
|
||||
border: 2px solid #2563eb;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
padding: 0.2rem 0.5rem;
|
||||
margin-right: 0.1rem;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background 0.18s,
|
||||
color 0.18s;
|
||||
}
|
||||
.create-btn:hover {
|
||||
background: #2563eb;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user