Merge branch 'develop' into feat/mail-integration
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
|
||||
<div>
|
||||
<AdminClientTab v-if="activeTab === 'clients'" />
|
||||
<AdminStatusTab v-if="activeTab === 'statuses'" />
|
||||
<AdminWorkflowTab v-if="activeTab === 'workflows'" />
|
||||
<AdminEffortTab v-if="activeTab === 'efforts'" />
|
||||
<AdminPriorityTab v-if="activeTab === 'priorities'" />
|
||||
<AdminTagTab v-if="activeTab === 'tags'" />
|
||||
@@ -41,7 +41,7 @@ useHead({ title: 'Administration' })
|
||||
|
||||
const tabs = [
|
||||
{ key: 'clients', label: 'Clients' },
|
||||
{ key: 'statuses', label: 'Statuts' },
|
||||
{ key: 'workflows', label: 'Workflows' },
|
||||
{ key: 'efforts', label: 'Efforts' },
|
||||
{ key: 'priorities', label: 'Priorités' },
|
||||
{ key: 'tags', label: 'Tags' },
|
||||
|
||||
168
frontend/pages/help.vue
Normal file
168
frontend/pages/help.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<script setup lang="ts">
|
||||
import { marked } from 'marked'
|
||||
|
||||
useHead({ title: 'Aide' })
|
||||
|
||||
type Section = {
|
||||
id: string
|
||||
title: string
|
||||
icon: string
|
||||
accent: string
|
||||
roles: ('admin' | 'user' | 'client')[]
|
||||
content: string
|
||||
}
|
||||
|
||||
const rawModules = import.meta.glob('~/content/help/*.md', { eager: true, query: '?raw', import: 'default' }) as Record<string, string>
|
||||
|
||||
const META: Record<string, { title: string, icon: string, accent: string, roles: ('admin' | 'user' | 'client')[] }> = {
|
||||
'01-getting-started': { title: 'Bienvenue', icon: 'mdi:hand-wave', accent: 'from-amber-400 to-rose-500', roles: ['admin', 'user', 'client'] },
|
||||
'02-projects-workflows': { title: 'Projets & Workflows', icon: 'mdi:view-column-outline', accent: 'from-indigo-500 to-fuchsia-500', roles: ['admin', 'user'] },
|
||||
'03-my-tasks': { title: 'Mes tâches', icon: 'mdi:checkbox-marked-circle-outline', accent: 'from-sky-500 to-cyan-500', roles: ['admin', 'user'] },
|
||||
'04-time-tracking': { title: 'Time tracking', icon: 'mdi:timer-outline', accent: 'from-emerald-500 to-teal-500', roles: ['admin', 'user'] },
|
||||
'05-tasks-detail': { title: 'Tâches en détail', icon: 'mdi:file-document-edit-outline', accent: 'from-violet-500 to-purple-600', roles: ['admin', 'user'] },
|
||||
'06-client-portal': { title: 'Portal client', icon: 'mdi:account-tie-outline', accent: 'from-orange-500 to-amber-500', roles: ['admin', 'client'] },
|
||||
'07-admin': { title: 'Administration', icon: 'mdi:shield-crown-outline', accent: 'from-rose-500 to-pink-600', roles: ['admin'] },
|
||||
'08-integrations': { title: 'Intégrations', icon: 'mdi:puzzle-outline', accent: 'from-blue-500 to-indigo-500', roles: ['admin', 'user'] },
|
||||
'09-mcp-api': { title: 'Token MCP & API', icon: 'mdi:robot-outline', accent: 'from-slate-700 to-slate-900', roles: ['admin', 'user'] },
|
||||
}
|
||||
|
||||
const sections = computed<Section[]>(() => {
|
||||
return Object.entries(rawModules).map(([path, raw]) => {
|
||||
const id = path.split('/').pop()!.replace(/\.md$/, '')
|
||||
const meta = META[id] ?? { title: id, icon: 'mdi:file-document-outline', accent: 'from-neutral-500 to-neutral-700', roles: ['admin', 'user', 'client'] as ('admin' | 'user' | 'client')[] }
|
||||
return { id, ...meta, content: raw }
|
||||
}).sort((a, b) => a.id.localeCompare(b.id))
|
||||
})
|
||||
|
||||
const auth = useAuthStore()
|
||||
|
||||
const userRole = computed<'admin' | 'user' | 'client'>(() => {
|
||||
const roles = auth.user?.roles ?? []
|
||||
if (roles.includes('ROLE_ADMIN')) return 'admin'
|
||||
if (roles.includes('ROLE_CLIENT')) return 'client'
|
||||
return 'user'
|
||||
})
|
||||
|
||||
const visibleSections = computed(() =>
|
||||
sections.value.filter(s => s.roles.includes(userRole.value)),
|
||||
)
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const activeId = ref(visibleSections.value[0]?.id ?? '')
|
||||
|
||||
onMounted(() => {
|
||||
const hash = (route.query.section as string) ?? route.hash.replace('#', '')
|
||||
if (hash && visibleSections.value.some(s => s.id === hash)) {
|
||||
activeId.value = hash
|
||||
}
|
||||
})
|
||||
|
||||
watch(activeId, (id) => {
|
||||
router.replace({ query: { ...route.query, section: id } })
|
||||
})
|
||||
|
||||
const activeSection = computed(() => visibleSections.value.find(s => s.id === activeId.value) ?? visibleSections.value[0])
|
||||
|
||||
const renderedHtml = computed(() => {
|
||||
if (!activeSection.value) return ''
|
||||
return marked.parse(activeSection.value.content, { async: false }) as string
|
||||
})
|
||||
|
||||
const prevSection = computed(() => {
|
||||
const idx = visibleSections.value.findIndex(s => s.id === activeId.value)
|
||||
return idx > 0 ? visibleSections.value[idx - 1] : null
|
||||
})
|
||||
|
||||
const nextSection = computed(() => {
|
||||
const idx = visibleSections.value.findIndex(s => s.id === activeId.value)
|
||||
return idx >= 0 && idx < visibleSections.value.length - 1 ? visibleSections.value[idx + 1] : null
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex min-h-[calc(100vh-60px)] flex-col lg:flex-row">
|
||||
<!-- Sidebar -->
|
||||
<aside class="shrink-0 border-b border-neutral-200 bg-gradient-to-b from-white to-neutral-50 px-3 py-4 lg:w-72 lg:border-b-0 lg:border-r lg:px-4 lg:py-6">
|
||||
<div class="mb-4 flex items-center gap-2 lg:mb-6">
|
||||
<div class="flex h-9 w-9 items-center justify-center rounded-xl bg-gradient-to-br from-primary-500 to-primary-700 text-white shadow-sm">
|
||||
<Icon name="mdi:lifebuoy" size="20" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-base font-bold text-neutral-900">Centre d'aide</h1>
|
||||
<p class="text-xs text-neutral-500">Lesstime — Guide utilisateur</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="flex flex-row gap-1 overflow-x-auto pb-1 lg:flex-col lg:overflow-visible lg:pb-0">
|
||||
<button
|
||||
v-for="section in visibleSections"
|
||||
:key="section.id"
|
||||
type="button"
|
||||
class="group flex shrink-0 items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm font-medium transition-all lg:shrink"
|
||||
:class="activeId === section.id
|
||||
? 'bg-white text-neutral-900 shadow-sm ring-1 ring-neutral-200'
|
||||
: 'text-neutral-600 hover:bg-white hover:text-neutral-900'"
|
||||
@click="activeId = section.id"
|
||||
>
|
||||
<span
|
||||
class="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br text-white shadow-sm"
|
||||
:class="section.accent"
|
||||
>
|
||||
<Icon :name="section.icon" size="16" />
|
||||
</span>
|
||||
<span class="whitespace-nowrap lg:whitespace-normal">{{ section.title }}</span>
|
||||
</button>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<!-- Content -->
|
||||
<main class="flex-1 px-4 py-6 sm:px-8 lg:px-12 lg:py-10">
|
||||
<div v-if="activeSection" class="mx-auto max-w-3xl">
|
||||
<!-- Hero header -->
|
||||
<div
|
||||
class="mb-8 overflow-hidden rounded-2xl bg-gradient-to-br p-6 text-white shadow-lg sm:p-8"
|
||||
:class="activeSection.accent"
|
||||
>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex h-14 w-14 shrink-0 items-center justify-center rounded-2xl bg-white/20 backdrop-blur-sm">
|
||||
<Icon :name="activeSection.icon" size="28" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-wider text-white/80">Section</p>
|
||||
<h2 class="text-2xl font-bold tracking-tight sm:text-3xl">{{ activeSection.title }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Markdown content -->
|
||||
<article
|
||||
class="prose prose-neutral max-w-none prose-headings:font-bold prose-headings:tracking-tight prose-h1:hidden prose-h2:mt-10 prose-h2:border-b prose-h2:border-neutral-200 prose-h2:pb-2 prose-h3:text-neutral-800 prose-a:text-primary-600 prose-strong:text-neutral-900 prose-code:rounded prose-code:bg-neutral-100 prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-code:font-medium prose-code:text-rose-600 prose-code:before:content-none prose-code:after:content-none prose-pre:rounded-xl prose-pre:bg-slate-900 prose-table:border prose-table:border-neutral-200 prose-th:bg-neutral-50 prose-th:px-3 prose-th:py-2 prose-td:px-3 prose-td:py-2 prose-blockquote:rounded-r-lg prose-blockquote:border-l-4 prose-blockquote:border-amber-400 prose-blockquote:bg-amber-50 prose-blockquote:px-4 prose-blockquote:py-2 prose-blockquote:not-italic prose-blockquote:text-amber-900"
|
||||
v-html="renderedHtml"
|
||||
/>
|
||||
|
||||
<!-- Footer nav -->
|
||||
<div class="mt-12 flex items-center justify-between border-t border-neutral-200 pt-6">
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center gap-2 rounded-lg px-3 py-2 text-sm text-neutral-600 transition-colors hover:bg-neutral-100 hover:text-neutral-900 disabled:invisible"
|
||||
:disabled="!prevSection"
|
||||
@click="prevSection && (activeId = prevSection.id)"
|
||||
>
|
||||
<Icon name="mdi:arrow-left" size="18" />
|
||||
<span>{{ prevSection?.title ?? '' }}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center gap-2 rounded-lg px-3 py-2 text-sm text-neutral-600 transition-colors hover:bg-neutral-100 hover:text-neutral-900 disabled:invisible"
|
||||
:disabled="!nextSection"
|
||||
@click="nextSection && (activeId = nextSection.id)"
|
||||
>
|
||||
<span>{{ nextSection?.title ?? '' }}</span>
|
||||
<Icon name="mdi:arrow-right" size="18" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
@@ -7,6 +7,8 @@ import type { TaskTag } from '~/services/dto/task-tag'
|
||||
import type { TaskGroup } from '~/services/dto/task-group'
|
||||
import type { UserData } from '~/services/dto/user-data'
|
||||
import type { Project } from '~/services/dto/project'
|
||||
import type { StatusCategory } from '~/services/dto/workflow'
|
||||
import { STATUS_CATEGORY_LABEL } from '~/services/dto/workflow'
|
||||
import { useTaskService } from '~/services/tasks'
|
||||
import { useTaskStatusService } from '~/services/task-statuses'
|
||||
import { useTaskEffortService } from '~/services/task-efforts'
|
||||
@@ -60,6 +62,7 @@ const viewMode = ref<'kanban' | 'list'>('kanban')
|
||||
|
||||
// Bulk selection
|
||||
const selectedTaskIds = reactive(new Set<number>())
|
||||
const selectedTasksArray = computed(() => tasks.value.filter(t => selectedTaskIds.has(t.id)))
|
||||
|
||||
// Modal
|
||||
const taskModalOpen = ref(false)
|
||||
@@ -112,13 +115,11 @@ const sortOptions = computed(() => [
|
||||
{ label: t('myTasks.sortScheduledStart'), value: SORT_SCHEDULED },
|
||||
])
|
||||
|
||||
// Kanban helpers
|
||||
const sortedStatuses = computed(() =>
|
||||
[...statuses.value].sort((a, b) => a.position - b.position)
|
||||
)
|
||||
// Kanban helpers (grouped by canonical status category)
|
||||
const CATEGORIES: StatusCategory[] = ['todo', 'in_progress', 'blocked', 'review', 'done']
|
||||
|
||||
function tasksByStatus(statusId: number): Task[] {
|
||||
return tasks.value.filter(t => t.status?.id === statusId)
|
||||
function tasksByCategory(category: StatusCategory): Task[] {
|
||||
return tasks.value.filter(t => t.status?.category === category)
|
||||
}
|
||||
|
||||
const backlogTasks = computed(() =>
|
||||
@@ -205,44 +206,6 @@ watch(selectedProjectId, () => {
|
||||
selectedGroupId.value = null
|
||||
}, { flush: 'sync' })
|
||||
|
||||
// Drag & drop
|
||||
const dragOverStatusId = ref<number | null>(null)
|
||||
const dragCounter = ref(0)
|
||||
|
||||
function onDragEnter(id: number) {
|
||||
dragCounter.value++
|
||||
dragOverStatusId.value = id
|
||||
}
|
||||
|
||||
function onDragLeave() {
|
||||
dragCounter.value--
|
||||
if (dragCounter.value === 0) {
|
||||
dragOverStatusId.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function onDrop(event: DragEvent) {
|
||||
dragCounter.value = 0
|
||||
dragOverStatusId.value = null
|
||||
return Number(event.dataTransfer!.getData('text/plain'))
|
||||
}
|
||||
|
||||
async function onDropStatus(event: DragEvent, status: TaskStatus) {
|
||||
const taskId = onDrop(event)
|
||||
const task = tasks.value.find(t => t.id === taskId)
|
||||
if (!task || task.status?.id === status.id) return
|
||||
task.status = status
|
||||
await taskService.update(taskId, { status: `/api/task_statuses/${status.id}` })
|
||||
}
|
||||
|
||||
async function onDropBacklog(event: DragEvent) {
|
||||
const taskId = onDrop(event)
|
||||
const task = tasks.value.find(t => t.id === taskId)
|
||||
if (!task || !task.status) return
|
||||
task.status = null
|
||||
await taskService.update(taskId, { status: null })
|
||||
}
|
||||
|
||||
// Modal
|
||||
function openTaskCreate() {
|
||||
selectedTask.value = null
|
||||
@@ -428,36 +391,29 @@ onMounted(async () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Kanban View -->
|
||||
<!-- Kanban View — grouped by canonical category -->
|
||||
<div v-if="viewMode === 'kanban'">
|
||||
<div class="mt-6 flex h-[calc(100vh-260px)] gap-3 overflow-x-auto pb-4">
|
||||
<div
|
||||
v-for="status in sortedStatuses"
|
||||
:key="status.id"
|
||||
class="flex min-w-36 flex-1 flex-col rounded-lg transition-colors"
|
||||
:class="dragOverStatusId === status.id ? 'bg-neutral-200' : 'bg-neutral-50'"
|
||||
@dragover.prevent
|
||||
@dragenter.prevent="onDragEnter(status.id)"
|
||||
@dragleave="onDragLeave"
|
||||
@drop.prevent="onDropStatus($event, status)"
|
||||
v-for="cat in CATEGORIES"
|
||||
:key="cat"
|
||||
class="flex min-w-40 flex-1 flex-col rounded-lg bg-neutral-50"
|
||||
>
|
||||
<div
|
||||
class="shrink-0 rounded-t-lg px-4 py-3 text-sm font-bold text-white"
|
||||
:style="{ backgroundColor: status.color }"
|
||||
>
|
||||
{{ status.label }} ({{ tasksByStatus(status.id).length }})
|
||||
<div class="shrink-0 rounded-t-lg bg-neutral-200 px-4 py-3 text-sm font-bold text-neutral-800">
|
||||
{{ STATUS_CATEGORY_LABEL[cat] }} ({{ tasksByCategory(cat).length }})
|
||||
</div>
|
||||
<div class="min-h-0 flex-1 overflow-y-auto p-3">
|
||||
<div class="flex flex-col gap-3">
|
||||
<TaskCard
|
||||
v-for="task in tasksByStatus(status.id)"
|
||||
v-for="task in tasksByCategory(cat)"
|
||||
:key="task.id"
|
||||
:task="task"
|
||||
show-project-color
|
||||
show-status-badge
|
||||
@click="openTaskEdit(task)"
|
||||
/>
|
||||
<p
|
||||
v-if="tasksByStatus(status.id).length === 0"
|
||||
v-if="tasksByCategory(cat).length === 0"
|
||||
class="py-4 text-center text-xs text-neutral-400"
|
||||
>
|
||||
{{ $t('myTasks.noTasks') }}
|
||||
@@ -467,15 +423,8 @@ onMounted(async () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Backlog below kanban -->
|
||||
<div
|
||||
class="mt-8 rounded-lg p-4 transition-colors"
|
||||
:class="dragOverStatusId === 0 ? 'bg-neutral-200' : 'bg-neutral-50'"
|
||||
@dragover.prevent
|
||||
@dragenter.prevent="onDragEnter(0)"
|
||||
@dragleave="onDragLeave"
|
||||
@drop.prevent="onDropBacklog($event)"
|
||||
>
|
||||
<!-- Backlog below kanban (no drag/drop — status change goes through TaskModal) -->
|
||||
<div class="mt-8 rounded-lg bg-neutral-50 p-4">
|
||||
<h2 class="text-lg font-bold text-neutral-900">{{ $t('myTasks.backlog') }} ({{ backlogTasks.length }})</h2>
|
||||
<div class="mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
<TaskCard
|
||||
@@ -483,6 +432,7 @@ onMounted(async () => {
|
||||
:key="task.id"
|
||||
:task="task"
|
||||
show-project-color
|
||||
show-status-badge
|
||||
@click="openTaskEdit(task)"
|
||||
/>
|
||||
</div>
|
||||
@@ -507,6 +457,8 @@ onMounted(async () => {
|
||||
:priorities="priorities"
|
||||
:efforts="efforts"
|
||||
:groups="groups"
|
||||
:selected-tasks="selectedTasksArray"
|
||||
:projects="projects"
|
||||
@toggle-all="toggleSelectAll(tasks)"
|
||||
@bulk-update="onBulkUpdate"
|
||||
@bulk-archive="onBulkArchive"
|
||||
|
||||
@@ -82,7 +82,6 @@ import type { TaskGroup } from '~/services/dto/task-group'
|
||||
import type { UserData } from '~/services/dto/user-data'
|
||||
import { useProjectService } from '~/services/projects'
|
||||
import { useTaskService } from '~/services/tasks'
|
||||
import { useTaskStatusService } from '~/services/task-statuses'
|
||||
import { useTaskEffortService } from '~/services/task-efforts'
|
||||
import { useTaskPriorityService } from '~/services/task-priorities'
|
||||
import { useTaskTagService } from '~/services/task-tags'
|
||||
@@ -96,7 +95,6 @@ useHead({ title: 'Archives' })
|
||||
|
||||
const projectService = useProjectService()
|
||||
const taskService = useTaskService()
|
||||
const statusService = useTaskStatusService()
|
||||
const effortService = useTaskEffortService()
|
||||
const priorityService = useTaskPriorityService()
|
||||
const tagService = useTaskTagService()
|
||||
@@ -105,8 +103,11 @@ const userService = useUserService()
|
||||
|
||||
const project = ref<Project | null>(null)
|
||||
const archivedTasks = ref<Task[]>([])
|
||||
const statuses = ref<TaskStatus[]>([])
|
||||
const efforts = ref<TaskEffort[]>([])
|
||||
|
||||
const statuses = computed<TaskStatus[]>(() =>
|
||||
[...(project.value?.workflow?.statuses ?? [])].sort((a, b) => a.position - b.position),
|
||||
)
|
||||
const priorities = ref<TaskPriority[]>([])
|
||||
const tags = ref<TaskTag[]>([])
|
||||
const groups = ref<TaskGroup[]>([])
|
||||
@@ -126,10 +127,9 @@ const filteredTasks = computed(() => {
|
||||
})
|
||||
|
||||
async function loadData() {
|
||||
const [p, t, s, e, pr, ty, g, u] = await Promise.all([
|
||||
const [p, t, e, pr, ty, g, u] = await Promise.all([
|
||||
projectService.getById(projectId.value),
|
||||
taskService.getByProject(projectId.value, true),
|
||||
statusService.getAll(),
|
||||
effortService.getAll(),
|
||||
priorityService.getAll(),
|
||||
tagService.getAll(),
|
||||
@@ -138,7 +138,6 @@ async function loadData() {
|
||||
])
|
||||
project.value = p
|
||||
archivedTasks.value = t
|
||||
statuses.value = s
|
||||
efforts.value = e
|
||||
priorities.value = pr
|
||||
tags.value = ty
|
||||
|
||||
@@ -218,7 +218,6 @@ import type { Client } from '~/services/dto/client'
|
||||
import { useProjectService } from '~/services/projects'
|
||||
import { useClientService } from '~/services/clients'
|
||||
import { useTaskService } from '~/services/tasks'
|
||||
import { useTaskStatusService } from '~/services/task-statuses'
|
||||
import { useTaskEffortService } from '~/services/task-efforts'
|
||||
import { useTaskPriorityService } from '~/services/task-priorities'
|
||||
import { useTaskTagService } from '~/services/task-tags'
|
||||
@@ -234,7 +233,6 @@ useHead({ title: 'Projet' })
|
||||
const projectService = useProjectService()
|
||||
const clientService = useClientService()
|
||||
const taskService = useTaskService()
|
||||
const statusService = useTaskStatusService()
|
||||
const effortService = useTaskEffortService()
|
||||
const priorityService = useTaskPriorityService()
|
||||
const tagService = useTaskTagService()
|
||||
@@ -243,7 +241,6 @@ const userService = useUserService()
|
||||
|
||||
const project = ref<Project | null>(null)
|
||||
const tasks = ref<Task[]>([])
|
||||
const statuses = ref<TaskStatus[]>([])
|
||||
const efforts = ref<TaskEffort[]>([])
|
||||
const priorities = ref<TaskPriority[]>([])
|
||||
const tags = ref<TaskTag[]>([])
|
||||
@@ -252,6 +249,10 @@ const users = ref<UserData[]>([])
|
||||
const clients = ref<Client[]>([])
|
||||
const isLoading = ref(true)
|
||||
|
||||
const statuses = computed<TaskStatus[]>(() =>
|
||||
[...(project.value?.workflow?.statuses ?? [])].sort((a, b) => a.position - b.position),
|
||||
)
|
||||
|
||||
const selectedGroupId = ref<number | null>(null)
|
||||
const selectedTagId = ref<number | null>(null)
|
||||
const selectedAssigneeId = ref<number | null>(null)
|
||||
@@ -333,10 +334,9 @@ const backlogTasks = computed(() =>
|
||||
async function loadData() {
|
||||
isLoading.value = true
|
||||
try {
|
||||
const [p, t, s, e, pr, ty, g, u, c] = await Promise.all([
|
||||
const [p, t, e, pr, ty, g, u, c] = await Promise.all([
|
||||
projectService.getById(projectId.value),
|
||||
taskService.getByProject(projectId.value),
|
||||
statusService.getAll(),
|
||||
effortService.getAll(),
|
||||
priorityService.getAll(),
|
||||
tagService.getAll(),
|
||||
@@ -346,7 +346,6 @@ async function loadData() {
|
||||
])
|
||||
project.value = p
|
||||
tasks.value = t
|
||||
statuses.value = s
|
||||
efforts.value = e
|
||||
priorities.value = pr
|
||||
tags.value = ty
|
||||
|
||||
Reference in New Issue
Block a user