feat: associated task status with task list not space
All checks were successful
Build and Push App Image / build-and-push (push) Successful in 1m52s

This commit is contained in:
domrichardson
2026-04-01 14:29:15 +01:00
parent 74d8899eec
commit 503d2415e6
12 changed files with 520 additions and 342 deletions

View File

@@ -0,0 +1,317 @@
<template>
<teleport to="body">
<div class="modal fade show d-block" tabindex="-1" role="dialog" aria-modal="true" @click.self="emit('close')">
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Task List</h5>
<button type="button" class="btn-close" aria-label="Close" @click="emit('close')"></button>
</div>
<div class="modal-body">
<!-- Task List Details -->
<div class="mb-4">
<label class="form-label" for="editTaskListName">Name</label>
<input id="editTaskListName" v-model="listForm.name" type="text" class="form-control" maxlength="120" />
<label class="form-label mt-3" for="editTaskListCategory">Category</label>
<select id="editTaskListCategory" v-model="listForm.category_id" class="form-select">
<option :value="null">No category</option>
<option v-for="cat in categoryOptions" :key="cat.id" :value="cat.id">{{ cat.label }}</option>
</select>
<button type="button" class="btn btn-primary mt-3" @click="saveListDetails">Save Details</button>
</div>
<hr />
<!-- Status Progression -->
<div class="mb-4">
<div class="d-flex justify-content-between align-items-center mb-2">
<strong>Status Progression</strong>
<button class="btn btn-sm btn-outline-primary" @click="openCreateStatusModal">Add Status</button>
</div>
<div class="status-list">
<div
v-for="status in statuses"
:key="status.id"
class="status-item"
:class="{ 'is-drag-over': dragOverStatusId === status.id }"
draggable="true"
@dragstart="onStatusDragStart(status.id)"
@dragover.prevent="onStatusDragOver(status.id)"
@dragleave="onStatusDragLeave(status.id)"
@drop.prevent="onStatusDrop(status.id)"
@dragend="onStatusDragEnd"
>
<span class="drag-handle" aria-hidden="true">
<i class="mdi mdi-drag-horizontal-variant"></i>
</span>
<span class="status-dot" :style="{ backgroundColor: status.color || '#7c8596' }"></span>
<span class="status-name">{{ status.name }}</span>
<div class="status-actions">
<button class="btn btn-sm btn-outline-secondary" @click="openEditStatusModal(status)">Edit</button>
</div>
</div>
</div>
</div>
<hr />
<!-- Danger Zone -->
<DangerZonePanel
v-if="canDeleteTaskList"
title-id="edit-task-list-danger-zone"
title="Danger Zone"
description="Delete this task list, all associated tasks, and statuses permanently."
>
<button type="button" class="btn btn-danger" @click="emit('delete-task-list', taskList)">
<i class="mdi mdi-delete-outline me-1" aria-hidden="true"></i>
Delete Task List
</button>
</DangerZonePanel>
</div>
</div>
</div>
</div>
<div class="modal-backdrop fade show"></div>
<!-- Status Create/Edit Sub-Modal -->
<div v-if="showStatusModal" class="modal fade show d-block" tabindex="-1" role="dialog" aria-modal="true" style="z-index: 1060" @click.self="closeStatusModal">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ statusMode === "create" ? "Create Task Status" : "Edit Task Status" }}</h5>
<button type="button" class="btn-close" aria-label="Close" @click="closeStatusModal"></button>
</div>
<div class="modal-body">
<label class="form-label" for="editStatusName">Status Name</label>
<input id="editStatusName" v-model="statusForm.name" type="text" class="form-control" maxlength="100" placeholder="e.g. Blocked" />
<label class="form-label mt-3" for="editStatusColor">Status Color</label>
<div class="status-color-row">
<input id="editStatusColor" v-model="statusForm.color" type="color" class="form-control form-control-color" title="Choose status color" />
<input v-model="statusForm.color" type="text" class="form-control" placeholder="#7c8596" maxlength="20" />
</div>
<DangerZonePanel
v-if="statusMode === 'edit'"
class="mt-4"
title-id="edit-status-danger-zone"
title="Danger Zone"
description="Deleting this status is permanent and cannot be undone."
>
<button type="button" class="btn btn-outline-danger" @click="deleteStatusFromModal">Delete Status</button>
</DangerZonePanel>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" @click="closeStatusModal">Cancel</button>
<button type="button" class="btn btn-primary" @click="submitStatusForm">
{{ statusMode === "create" ? "Create" : "Save" }}
</button>
</div>
</div>
</div>
</div>
<div v-if="showStatusModal" class="modal-backdrop fade show" style="z-index: 1055"></div>
</teleport>
</template>
<script setup>
import { ref, watch } from "vue";
import DangerZonePanel from "./DangerZonePanel.vue";
const props = defineProps({
taskList: {
type: Object,
required: true,
},
statuses: {
type: Array,
default: () => [],
},
categoryOptions: {
type: Array,
default: () => [],
},
canDeleteTaskList: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["close", "update-task-list", "reorder-status", "create-status", "rename-status", "delete-status", "delete-task-list"]);
const listForm = ref({ name: "", category_id: null });
watch(
() => props.taskList,
(tl) => {
listForm.value = {
name: tl?.name || "",
category_id: tl?.category_id || null,
};
},
{ immediate: true },
);
const saveListDetails = () => {
const name = listForm.value.name?.trim();
if (!name) {
return;
}
emit("update-task-list", {
name,
category_id: listForm.value.category_id || null,
});
};
// Status drag-and-drop reorder
const draggedStatusId = ref("");
const dragOverStatusId = ref("");
const onStatusDragStart = (id) => {
draggedStatusId.value = id;
};
const onStatusDragOver = (id) => {
dragOverStatusId.value = id;
};
const onStatusDragLeave = (id) => {
if (dragOverStatusId.value === id) {
dragOverStatusId.value = "";
}
};
const onStatusDrop = (targetId) => {
if (!draggedStatusId.value || draggedStatusId.value === targetId) {
onStatusDragEnd();
return;
}
const ordered = props.statuses.map((s) => s.id);
const fromIndex = ordered.indexOf(draggedStatusId.value);
const targetIndex = ordered.indexOf(targetId);
if (fromIndex < 0 || targetIndex < 0) {
onStatusDragEnd();
return;
}
ordered.splice(fromIndex, 1);
const insertIndex = ordered.indexOf(targetId);
ordered.splice(insertIndex, 0, draggedStatusId.value);
emit("reorder-status", ordered);
onStatusDragEnd();
};
const onStatusDragEnd = () => {
draggedStatusId.value = "";
dragOverStatusId.value = "";
};
// Status create/edit modal
const showStatusModal = ref(false);
const statusMode = ref("create");
const editingStatusId = ref("");
const statusForm = ref({ name: "", color: "#7c8596" });
const openCreateStatusModal = () => {
statusMode.value = "create";
editingStatusId.value = "";
statusForm.value = { name: "", color: "#7c8596" };
showStatusModal.value = true;
};
const openEditStatusModal = (status) => {
statusMode.value = "edit";
editingStatusId.value = status.id;
statusForm.value = { name: status.name || "", color: status.color || "#7c8596" };
showStatusModal.value = true;
};
const closeStatusModal = () => {
showStatusModal.value = false;
statusMode.value = "create";
editingStatusId.value = "";
statusForm.value = { name: "", color: "#7c8596" };
};
const submitStatusForm = () => {
const name = statusForm.value.name?.trim();
if (!name) {
return;
}
const color = statusForm.value.color?.trim() || "";
if (statusMode.value === "create") {
emit("create-status", { name, color });
} else if (editingStatusId.value) {
emit("rename-status", { id: editingStatusId.value, name, color });
}
closeStatusModal();
};
const deleteStatusFromModal = () => {
if (statusMode.value !== "edit" || !editingStatusId.value) {
return;
}
emit("delete-status", {
id: editingStatusId.value,
name: statusForm.value.name?.trim() || "",
color: statusForm.value.color?.trim() || "",
});
closeStatusModal();
};
</script>
<style scoped>
.status-list {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.status-item {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.35rem 0.5rem;
border-radius: 0.375rem;
border: 1px solid var(--bs-border-color);
background: var(--bs-body-bg);
cursor: grab;
}
.status-item.is-drag-over {
border-color: var(--bs-primary);
background: rgba(var(--bs-primary-rgb), 0.08);
}
.drag-handle {
cursor: grab;
opacity: 0.5;
font-size: 1.1rem;
line-height: 1;
}
.status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
.status-name {
flex: 1;
font-size: 0.9rem;
}
.status-actions {
margin-left: auto;
}
.status-color-row {
display: flex;
gap: 0.5rem;
align-items: center;
}
.status-color-row .form-control-color {
width: 40px;
padding: 0.25rem;
flex-shrink: 0;
}
</style>

View File

@@ -5,6 +5,7 @@
<h4 class="mb-0">Tasks</h4>
<p class="text-muted small mb-0">Track work with ordered statuses.</p>
</div>
<button v-if="selectedTaskList" class="btn btn-sm btn-outline-secondary" @click="emit('edit-task-list')"><i class="mdi mdi-cog-outline me-1" aria-hidden="true"></i>Edit Task List</button>
</div>
<div class="task-filters">
@@ -23,36 +24,6 @@
</select>
</div>
<div class="status-lane">
<div class="lane-header">
<strong>Status Progression</strong>
<button class="btn btn-sm btn-outline-primary" @click="openCreateStatusModal">Add Status</button>
</div>
<div class="status-list">
<div
v-for="status in statuses"
:key="status.id"
class="status-item"
:class="{ 'is-drag-over': dragOverStatusId === status.id }"
draggable="true"
@dragstart="onStatusDragStart(status.id)"
@dragover.prevent="onStatusDragOver(status.id)"
@dragleave="onStatusDragLeave(status.id)"
@drop.prevent="onStatusDrop(status.id)"
@dragend="onStatusDragEnd"
>
<span class="drag-handle" aria-hidden="true">
<i class="mdi mdi-drag-horizontal-variant"></i>
</span>
<span class="status-dot" :style="{ backgroundColor: status.color || '#7c8596' }"></span>
<span class="status-name">{{ status.name }}</span>
<div class="status-actions">
<button class="btn btn-sm btn-outline-secondary" @click="openEditStatusModal(status)">Edit</button>
</div>
</div>
</div>
</div>
<div class="task-status-groups">
<div v-if="!tasks.length" class="empty-state">No tasks matched these filters.</div>
@@ -184,65 +155,11 @@
</div>
</section>
</div>
<DangerZonePanel
v-if="selectedTaskList && canDeleteTaskList"
title-id="task-list-danger-zone-title"
title="Danger Zone"
description="Delete this task list and all associated tasks permanently."
>
<button type="button" class="btn btn-danger" @click="emitDeleteTaskList">
<i class="mdi mdi-delete-outline me-1" aria-hidden="true"></i>
Delete Task List
</button>
</DangerZonePanel>
<teleport to="body">
<div v-if="showStatusModal" class="modal fade show d-block" tabindex="-1" role="dialog" aria-modal="true" @click.self="closeStatusModal">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ statusMode === "create" ? "Create Task Status" : "Edit Task Status" }}</h5>
<button type="button" class="btn-close" aria-label="Close" @click="closeStatusModal"></button>
</div>
<div class="modal-body">
<label class="form-label" for="taskStatusName">Status Name</label>
<input id="taskStatusName" v-model="statusForm.name" type="text" class="form-control" maxlength="100" placeholder="e.g. Blocked" />
<label class="form-label mt-3" for="taskStatusColor">Status Color</label>
<div class="status-color-row">
<input id="taskStatusColor" v-model="statusForm.color" type="color" class="form-control form-control-color" title="Choose status color" />
<input v-model="statusForm.color" type="text" class="form-control" placeholder="#7c8596" maxlength="20" />
</div>
<DangerZonePanel
v-if="statusMode === 'edit'"
class="mt-4"
title-id="status-danger-zone-title"
title="Danger Zone"
description="Deleting this status is permanent and cannot be undone."
copy-class="mb-2"
>
<button type="button" class="btn btn-outline-danger" @click="deleteStatusFromModal">Delete Status</button>
</DangerZonePanel>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" @click="closeStatusModal">Cancel</button>
<button type="button" class="btn btn-primary" @click="submitStatusForm">
{{ statusMode === "create" ? "Create" : "Save" }}
</button>
</div>
</div>
</div>
</div>
<div v-if="showStatusModal" class="modal-backdrop fade show"></div>
</teleport>
</section>
</template>
<script setup>
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
import DangerZonePanel from "./DangerZonePanel.vue";
const props = defineProps({
tasks: {
@@ -257,27 +174,14 @@ const props = defineProps({
type: Object,
default: null,
},
canDeleteTaskList: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["create-task", "select-task", "filter-change", "reorder-status", "create-status", "rename-status", "delete-status", "update-task-status", "delete-task-list"]);
const emit = defineEmits(["create-task", "select-task", "filter-change", "update-task-status", "edit-task-list"]);
const filterStatus = ref("");
const filterParent = ref("");
const showStatusModal = ref(false);
const statusMode = ref("create");
const editingStatusId = ref("");
const draggedStatusId = ref("");
const dragOverStatusId = ref("");
const expandedTaskIds = ref({});
const openStatusMenuTaskId = ref("");
const statusForm = ref({
name: "",
color: "#7c8596",
});
const parentTaskOptions = computed(() => props.tasks.filter((task) => task.depth < 2));
const tasksById = computed(() => {
@@ -389,120 +293,6 @@ onMounted(() => {
onBeforeUnmount(() => {
document.removeEventListener("click", onDocumentClick);
});
const onStatusDragStart = (statusId) => {
draggedStatusId.value = statusId;
};
const onStatusDragOver = (statusId) => {
dragOverStatusId.value = statusId;
};
const onStatusDragLeave = (statusId) => {
if (dragOverStatusId.value === statusId) {
dragOverStatusId.value = "";
}
};
const onStatusDrop = (targetStatusId) => {
if (!draggedStatusId.value || draggedStatusId.value === targetStatusId) {
onStatusDragEnd();
return;
}
const ordered = props.statuses.map((item) => item.id);
const fromIndex = ordered.indexOf(draggedStatusId.value);
const targetIndex = ordered.indexOf(targetStatusId);
if (fromIndex < 0 || targetIndex < 0) {
onStatusDragEnd();
return;
}
ordered.splice(fromIndex, 1);
const insertIndex = ordered.indexOf(targetStatusId);
ordered.splice(insertIndex, 0, draggedStatusId.value);
emit("reorder-status", ordered);
onStatusDragEnd();
};
const onStatusDragEnd = () => {
draggedStatusId.value = "";
dragOverStatusId.value = "";
};
const closeStatusModal = () => {
showStatusModal.value = false;
statusMode.value = "create";
editingStatusId.value = "";
statusForm.value = {
name: "",
color: "#7c8596",
};
};
const openCreateStatusModal = () => {
statusMode.value = "create";
editingStatusId.value = "";
statusForm.value = {
name: "",
color: "#7c8596",
};
showStatusModal.value = true;
};
const openEditStatusModal = (status) => {
statusMode.value = "edit";
editingStatusId.value = status.id;
statusForm.value = {
name: status.name || "",
color: status.color || "#7c8596",
};
showStatusModal.value = true;
};
const submitStatusForm = () => {
const name = statusForm.value.name?.trim();
if (!name) {
return;
}
const color = statusForm.value.color?.trim() || "";
if (statusMode.value === "create") {
emit("create-status", { name, color });
} else {
if (!editingStatusId.value) {
return;
}
emit("rename-status", {
id: editingStatusId.value,
name,
color,
});
}
closeStatusModal();
};
const deleteStatusFromModal = () => {
if (statusMode.value !== "edit" || !editingStatusId.value) {
return;
}
emit("delete-status", {
id: editingStatusId.value,
name: statusForm.value.name?.trim() || "",
color: statusForm.value.color?.trim() || "",
});
closeStatusModal();
};
const emitDeleteTaskList = () => {
if (!props.selectedTaskList?.id || !props.canDeleteTaskList) {
return;
}
emit("delete-task-list", props.selectedTaskList);
};
</script>
<style scoped src="../assets/styles/scoped/components/TaskBoard.css"></style>

View File

@@ -5,15 +5,10 @@
:tasks="tasks"
:statuses="taskStatuses"
:selected-task-list="selectedTaskList"
:can-delete-task-list="canDeleteTasks"
@select-task="emit('select-task', $event)"
@filter-change="emit('filter-change', $event)"
@reorder-status="emit('reorder-status', $event)"
@create-status="emit('create-status', $event)"
@rename-status="emit('rename-status', $event)"
@delete-status="emit('delete-status', $event)"
@update-task-status="emit('update-task-status', $event)"
@delete-task-list="emit('delete-task-list', $event)"
@edit-task-list="emit('edit-task-list')"
/>
<SearchResultsPage
v-else-if="isSearchRoute"
@@ -151,12 +146,8 @@ defineProps({
const emit = defineEmits([
"select-task",
"filter-change",
"reorder-status",
"create-status",
"rename-status",
"delete-status",
"update-task-status",
"delete-task-list",
"edit-task-list",
"select-note",
"select-task-list",
"page-change",

View File

@@ -202,7 +202,6 @@
:tasks="tasks"
:task-statuses="taskStatuses"
:selected-task-list="selectedTaskList"
:can-delete-tasks="canDeleteTasks"
:is-search-route="isSearchRoute"
:search-items="searchItems"
:search-query="searchQuery"
@@ -220,12 +219,8 @@
:is-loading-more-main-notes="spaceStore.notesLoading"
@select-task="openTaskDetail"
@filter-change="applyTaskFilters"
@reorder-status="reorderTaskStatuses"
@create-status="createTaskStatus"
@rename-status="renameTaskStatus"
@delete-status="requestDeleteTaskStatus"
@update-task-status="updateTaskStatusFromBoard"
@delete-task-list="requestRemoveTaskList"
@edit-task-list="showEditTaskListModal = true"
@select-note="selectSearchResultNote"
@select-task-list="selectTaskList"
@page-change="setSearchPage"
@@ -319,6 +314,21 @@
<div v-if="showUnlockModal" class="modal-backdrop fade show"></div>
</teleport>
<EditTaskListModal
v-if="showEditTaskListModal && selectedTaskList"
:task-list="selectedTaskList"
:statuses="taskStatuses"
:category-options="categoryOptions"
:can-delete-task-list="canDeleteTasks"
@close="showEditTaskListModal = false"
@update-task-list="updateTaskListFromModal"
@reorder-status="reorderTaskStatuses"
@create-status="createTaskStatus"
@rename-status="renameTaskStatus"
@delete-status="requestDeleteTaskStatus"
@delete-task-list="requestRemoveTaskList"
/>
<ConfirmActionModal
:visible="showTaskDeleteConfirmModal"
:title="taskDeleteConfirmTitle"
@@ -339,6 +349,7 @@ import CategoryTree from "../components/CategoryTree.vue";
import AppWorkspaceContent from "../components/app/AppWorkspaceContent.vue";
import AppModalHost from "../components/app/AppModalHost.vue";
import ConfirmActionModal from "../components/ConfirmActionModal.vue";
import EditTaskListModal from "../components/EditTaskListModal.vue";
import { sortNotesByPriority } from "../utils/noteSort";
import apiClient from "../services/apiClient";
@@ -354,6 +365,7 @@ const showCreateSpaceModal = ref(false);
const showCreateCategoryModal = ref(false);
const showCreateNoteModal = ref(false);
const showCreateTaskListModal = ref(false);
const showEditTaskListModal = ref(false);
const showSidebar = ref(false);
const navbarRef = ref(null);
const navbarHeight = ref(56);
@@ -819,6 +831,7 @@ watch(
isEditingNote.value = false;
selectedCategory.value = null;
selectedTaskList.value = taskLists.value.find((list) => list.id === taskListId) || null;
await spaceStore.fetchTaskStatuses(currentSpace.value?.id, taskListId);
await applyTaskFilters({ taskListId });
return;
}
@@ -1056,6 +1069,7 @@ const selectTaskList = async (taskList) => {
showSidebar.value = false;
if (currentSpace.value?.id && taskList?.id) {
await router.push(dashboardTaskRoute(currentSpace.value.id, taskList.id));
await spaceStore.fetchTaskStatuses(currentSpace.value.id, taskList.id);
}
await applyTaskFilters({
@@ -1253,22 +1267,22 @@ const createSubtask = (parentTask) => {
};
const createTaskStatus = async (payload) => {
if (!currentSpace.value?.id) {
if (!currentSpace.value?.id || !selectedTaskList.value?.id) {
return;
}
try {
await spaceStore.createTaskStatus(currentSpace.value.id, payload);
await spaceStore.createTaskStatus(currentSpace.value.id, selectedTaskList.value.id, payload);
} catch (error) {
alert(error?.response?.data || "Unable to create status.");
}
};
const renameTaskStatus = async (status) => {
if (!currentSpace.value?.id || !status?.id) {
if (!currentSpace.value?.id || !selectedTaskList.value?.id || !status?.id) {
return;
}
try {
await spaceStore.updateTaskStatus(currentSpace.value.id, status.id, {
await spaceStore.updateTaskStatus(currentSpace.value.id, selectedTaskList.value.id, status.id, {
name: status.name,
color: status.color,
});
@@ -1300,12 +1314,12 @@ const requestDeleteTaskStatus = (status) => {
};
const deleteTaskStatus = async (status) => {
if (!currentSpace.value?.id || !status?.id) {
if (!currentSpace.value?.id || !selectedTaskList.value?.id || !status?.id) {
return;
}
try {
await spaceStore.deleteTaskStatus(currentSpace.value.id, status.id);
await spaceStore.deleteTaskStatus(currentSpace.value.id, selectedTaskList.value.id, status.id);
} catch (error) {
alert(error?.response?.data || "Unable to delete status.");
throw error;
@@ -1313,11 +1327,11 @@ const deleteTaskStatus = async (status) => {
};
const reorderTaskStatuses = async (orderedIds) => {
if (!currentSpace.value?.id) {
if (!currentSpace.value?.id || !selectedTaskList.value?.id) {
return;
}
try {
await spaceStore.reorderTaskStatuses(currentSpace.value.id, orderedIds);
await spaceStore.reorderTaskStatuses(currentSpace.value.id, selectedTaskList.value.id, orderedIds);
} catch (error) {
alert(error?.response?.data || "Unable to reorder statuses.");
}
@@ -1390,10 +1404,25 @@ const createTaskList = async (taskListData) => {
}
};
const updateTaskListFromModal = async (payload) => {
if (!currentSpace.value?.id || !selectedTaskList.value?.id) return;
try {
await spaceStore.updateTaskList(currentSpace.value.id, selectedTaskList.value.id, {
name: payload.name,
description: payload.description,
category_id: payload.category_id,
});
selectedTaskList.value = { ...selectedTaskList.value, name: payload.name, category_id: payload.category_id };
} catch (error) {
alert(error?.response?.data || "Unable to update task list.");
}
};
const requestRemoveTaskList = (taskList) => {
if (!currentSpace.value?.id || !taskList?.id || !canDeleteTasks.value) {
return;
}
showEditTaskListModal.value = false;
taskDeleteIntent.value = {
type: "task-list",
payload: taskList,

View File

@@ -19,7 +19,7 @@ export const useSpaceStore = defineStore("space", () => {
const noteLinkedTasks = ref([]);
const refreshSpaceData = async (spaceId) => {
await Promise.all([fetchCategories(spaceId), fetchNotes(spaceId), fetchTaskLists(spaceId), fetchTaskStatuses(spaceId), fetchTasks(spaceId)]);
await Promise.all([fetchCategories(spaceId), fetchNotes(spaceId), fetchTaskLists(spaceId), fetchTasks(spaceId)]);
};
const fetchSpaces = async () => {
@@ -212,13 +212,13 @@ export const useSpaceStore = defineStore("space", () => {
searchResults.value = [];
};
const fetchTaskStatuses = async (spaceId) => {
if (!spaceId) {
const fetchTaskStatuses = async (spaceId, taskListId) => {
if (!spaceId || !taskListId) {
taskStatuses.value = [];
return [];
}
try {
const response = await apiClient.get(`/api/v1/spaces/${spaceId}/task-statuses`);
const response = await apiClient.get(`/api/v1/spaces/${spaceId}/task-lists/${taskListId}/statuses`);
taskStatuses.value = response.data || [];
return taskStatuses.value;
} catch (error) {
@@ -261,25 +261,25 @@ export const useSpaceStore = defineStore("space", () => {
await fetchTaskLists(spaceId);
};
const createTaskStatus = async (spaceId, payload) => {
const response = await apiClient.post(`/api/v1/spaces/${spaceId}/task-statuses`, payload);
await fetchTaskStatuses(spaceId);
const createTaskStatus = async (spaceId, taskListId, payload) => {
const response = await apiClient.post(`/api/v1/spaces/${spaceId}/task-lists/${taskListId}/statuses`, payload);
await fetchTaskStatuses(spaceId, taskListId);
return response.data;
};
const updateTaskStatus = async (spaceId, statusId, payload) => {
const response = await apiClient.put(`/api/v1/spaces/${spaceId}/task-statuses/${statusId}`, payload);
await fetchTaskStatuses(spaceId);
const updateTaskStatus = async (spaceId, taskListId, statusId, payload) => {
const response = await apiClient.put(`/api/v1/spaces/${spaceId}/task-lists/${taskListId}/statuses/${statusId}`, payload);
await fetchTaskStatuses(spaceId, taskListId);
return response.data;
};
const deleteTaskStatus = async (spaceId, statusId) => {
await apiClient.delete(`/api/v1/spaces/${spaceId}/task-statuses/${statusId}`);
await fetchTaskStatuses(spaceId);
const deleteTaskStatus = async (spaceId, taskListId, statusId) => {
await apiClient.delete(`/api/v1/spaces/${spaceId}/task-lists/${taskListId}/statuses/${statusId}`);
await fetchTaskStatuses(spaceId, taskListId);
};
const reorderTaskStatuses = async (spaceId, orderedStatusIds) => {
const response = await apiClient.put(`/api/v1/spaces/${spaceId}/task-statuses/reorder`, {
const reorderTaskStatuses = async (spaceId, taskListId, orderedStatusIds) => {
const response = await apiClient.put(`/api/v1/spaces/${spaceId}/task-lists/${taskListId}/statuses/reorder`, {
ordered_status_ids: orderedStatusIds,
});
taskStatuses.value = response.data || [];