Skip to content
Merged

Tasks #202

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
62c93c3
Tasks module structure and page layout
gaspergrom Nov 5, 2022
ceabc31
Tasks add/edit form
gaspergrom Nov 5, 2022
836c90d
Tasks: optimizations
gaspergrom Nov 5, 2022
516a20a
Merge branch 'main' into feature/tasks
gaspergrom Nov 8, 2022
a807b12
Tasks creation / listing
gaspergrom Nov 8, 2022
d4f0a14
Tasks: listing/editing/deletion
gaspergrom Nov 8, 2022
660a971
Tasks completion and loading optimization
gaspergrom Nov 9, 2022
66ce3da
Tasks archive & data fetching
gaspergrom Nov 9, 2022
a350dda
Merge branch 'main' into feature/tasks
gaspergrom Nov 9, 2022
3931c6f
Tasks - dashboard widget
gaspergrom Nov 9, 2022
ec3e66a
Tasks: member tasks & suggested & assignees
gaspergrom Nov 9, 2022
56b90d1
tasks start
epipav Nov 8, 2022
a105d5e
tests are passing
epipav Nov 10, 2022
d7f5897
formatting
epipav Nov 10, 2022
ce74811
documentations
epipav Nov 10, 2022
7120c11
formatting, error handling
epipav Nov 10, 2022
61d9118
formatting
epipav Nov 10, 2022
2b1c5ac
updated endpoint routes
epipav Nov 10, 2022
ac1be17
typo in comment fixed
epipav Nov 10, 2022
5de5c8b
user repository autocomplete missing fullname fixed
epipav Nov 10, 2022
b2a7ecd
Merge branch 'enhancement/tasks-multiple-assignees' into feature/tasks
gaspergrom Nov 10, 2022
e3ed2d1
Tasks backend connect & permissions
gaspergrom Nov 10, 2022
28200c0
Tasks design fixes and adjustments
gaspergrom Nov 10, 2022
60150b2
findAndDeleteAll batch operation added to tasks
epipav Nov 11, 2022
b175eab
Merge branch 'enhancement/tasks-multiple-assignees' into feature/tasks
gaspergrom Nov 11, 2022
bf26b39
Tasks permissions
gaspergrom Nov 11, 2022
9c13048
Tasks - create permissions on widgets
gaspergrom Nov 11, 2022
ac08365
Fix linting
gaspergrom Nov 11, 2022
45326d2
Merge branch 'main' into feature/tasks
gaspergrom Nov 11, 2022
fb16740
Merge branch 'main' into feature/tasks
gaspergrom Nov 11, 2022
7f929d8
Activities bug fixes and improvements
gaspergrom Nov 11, 2022
98b6cca
Avatar images related members
gaspergrom Nov 11, 2022
96e24c3
Merge branch 'main' into feature/tasks
gaspergrom Nov 14, 2022
378dc08
Testing tasks
gaspergrom Nov 14, 2022
0a8bc25
Merge branch 'main' into feature/tasks
mariobalca Nov 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/src/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default {

created() {
this.doInit()
this.taskDoInit()
window.addEventListener('resize', this.handleResize)
this.handleResize()
},
Expand All @@ -35,6 +36,7 @@ export default {
methods: {
...mapActions({
doInit: 'auth/doInit',
taskDoInit: 'task/doInit',
resize: 'layout/resize'
}),

Expand Down
4 changes: 4 additions & 0 deletions frontend/src/assets/scss/dropdown.scss
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,7 @@
border-radius: 7px;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25);
}

.el-select-dropdown.is-multiple .el-select-dropdown__item.selected, .el-select-dropdown .el-select-dropdown__item.selected {
@apply text-brand-500;
}
9 changes: 9 additions & 0 deletions frontend/src/modules/activity/components/activity-content.vue
Original file line number Diff line number Diff line change
Expand Up @@ -156,5 +156,14 @@ export default {
a {
@apply text-brand-500;
}

h1,
h2,
h3,
h4,
h5,
h6 {
font-size: 16px;
}
}
</style>
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
<template>
<div
class="flex flex-wrap justify-between border-b border-gray-200"
>
<div class="flex flex-wrap justify-between">
<div class="pb-2">
<h4 class="text-xl font-semibold leading-9">
Overview of
<span class="text-brand-500">{{
currentTenant.name
}}</span>
</h4>
<app-dashboard-filters />
</div>
<div class="w-full lg:w-auto md:w-auto pb-2">
<p
Expand All @@ -27,11 +24,9 @@ import AppDashboardIntegrations from '@/modules/dashboard/components/dashboard-a
import { mapGetters } from 'vuex'
import moment from 'moment'
import { activitiesChart } from '@/modules/dashboard/dashboard.cube'
import AppDashboardFilters from '@/modules/dashboard/components/dashboard-filters'
export default {
name: 'AppDashboardHeader',
components: {
AppDashboardFilters,
AppDashboardIntegrations
},
computed: {
Expand Down
294 changes: 294 additions & 0 deletions frontend/src/modules/dashboard/components/dashboard-task.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
<template>
<div
v-if="hasPermissionToTask || isTaskLocked"
class="panel !p-0"
>
<!-- header -->
<div
class="flex items-center justify-between px-6 py-5"
>
<div class="flex items-center">
<div
class="w-8 h-8 rounded-md bg-gray-900 flex items-center justify-center mr-4"
>
<i
class="ri-checkbox-multiple-line text-lg text-white"
></i>
</div>
<h6 class="text-sm font-semibold leading-5">
My open tasks
</h6>
</div>
<div class="flex items-center">
<router-link
:to="{ name: 'task' }"
class="text-xs text-brand-500 font-medium mr-6"
>
All tasks
</router-link>
<button
v-if="taskCreatePermission"
class="btn btn--secondary btn--sm"
@click="addTask()"
>
Add task
</button>
</div>
</div>
<div
class="flex-grow overflow-auto widget-content pb-4"
>
<div v-if="loading">
<app-task-item
class="px-6"
:loading="true"
></app-task-item>
<app-task-item
class="px-6"
:loading="true"
></app-task-item>
</div>
<div v-else>
<app-task-item
v-for="task of tasks"
:key="task.id"
class="px-6"
:task="task"
/>
<div v-if="tasks.length === 0">
<div
v-if="suggestedTasks.length > 0"
class="pt-2 px-6"
>
<p
class="text-2xs font-semibold uppercase text-gray-400 tracking-1 pb-4"
>
Task suggestions:
</p>
<div class="flex flex-wrap -mx-2">
<div
v-for="suggested of suggestedTasks"
:key="suggested.id"
class="w-full md:w-1/3 lg:w-1/3 px-2 pb-2"
>
<article
class="border border-gray-200 rounded-lg p-4 h-full flex-grow"
>
<h6
class="text-2xs leading-4.5 font-semibold pb-1"
>
{{ suggested.name }}
</h6>
<div
class="text-xs leading-5 text-gray-500 pb-3 c-content"
v-html="$sanitize(suggested.body)"
></div>
<div
class="text-xs font-medium leading-5 text-brand-500 cursor-pointer"
@click="addSuggested(suggested)"
>
+ Add task
</div>
</article>
</div>
</div>
</div>
<div
v-else
class="pt-3 pb-4 flex justify-center items-center"
>
<div
class="ri-checkbox-multiple-blank-line text-3xl h-10 flex items-center text-gray-300"
></div>
<div
class="pl-6 text-sm leading-5 italic text-gray-400"
>
No tasks assigned to you at this moment
</div>
</div>
</div>

<div
v-if="tasks.length < taskCount"
class="flex justify-center pt-8 pb-1"
>
<div
class="flex items-center cursor-pointer"
@click="fetchTasks(true)"
>
<div
class="ri-arrow-down-line text-base text-brand-500 flex items-center h-4"
></div>
<div
class="pl-2 text-xs leading-5 text-brand-500 font-medium"
>
Load more
</div>
</div>
</div>
</div>
</div>
</div>
<app-task-form
v-model="openForm"
:task="selectedTask"
@close="task = null"
/>
</template>

<script>
export default {
name: 'AppDashboardTask'
}
</script>

<script setup>
import AppTaskForm from '@/modules/task/components/task-form'
import {
onBeforeUnmount,
ref,
onMounted,
computed
} from 'vue'
import { TaskService } from '@/modules/task/task-service'
import Message from '@/shared/message/message'
import { useStore } from 'vuex'
import AppTaskItem from '@/modules/task/components/task-item'
import { mapGetters } from '@/shared/vuex/vuex.helpers'
import { TaskPermissions } from '@/modules/task/task-permissions'

const store = useStore()
const { currentUser, currentTenant } = mapGetters('auth')

const openForm = ref(false)
const selectedTask = ref(null)
const tasks = ref([])
const suggestedTasks = ref([])
const taskCount = ref(0)
const loading = ref(false)
const intitialLoad = ref(false)

const addTask = () => {
openForm.value = true
selectedTask.value = null
}

const editTask = (task) => {
openForm.value = true
selectedTask.value = task
}

const isTaskLocked = computed(
() =>
new TaskPermissions(
currentTenant.value,
currentUser.value
).lockedForCurrentPlan
)
const hasPermissionToTask = computed(
() =>
new TaskPermissions(
currentTenant.value,
currentUser.value
).read
)

const taskCreatePermission = computed(
() =>
new TaskPermissions(
currentTenant.value,
currentUser.value
).create
)

const storeUnsubscribe = store.subscribeAction((action) => {
if (action.type === 'task/reloadOpenTasks') {
fetchTasks()
}
if (action.type === 'task/reloadSuggestedTasks') {
fetchSuggestedTasks()
}
if (action.type === 'task/addTask') {
addTask()
}
if (action.type === 'task/editTask') {
editTask(action.payload)
}
})

const addSuggested = (task) => {
editTask({
...task,
assignees: [currentUser.value]
})
}

onMounted(() => {
fetchSuggestedTasks()
fetchTasks()
})

onBeforeUnmount(() => {
storeUnsubscribe()
})

const fetchTasks = (loadMore = false) => {
if (!intitialLoad.value) {
loading.value = true
}

TaskService.list(
{
type: 'regular',
status: 'in-progress',
assignees: [currentUser.value.id]
},
'dueDate_ASC',
20,
loadMore ? tasks.value.length : 0
)
.then(({ rows, count }) => {
tasks.value = loadMore
? [...tasks.value, ...rows]
: rows
taskCount.value = count
})
.catch(() => {
if (!loadMore) {
tasks.value = []
}
Message.error('There was an error loading tasks')
})
.finally(() => {
loading.value = false
intitialLoad.value = true
})
}

const fetchSuggestedTasks = () => {
TaskService.list(
{
type: 'suggested'
},
'createdAt_DESC',
3,
0
)
.then(({ rows }) => {
suggestedTasks.value = rows
})
.catch(() => {
Message.error('There was an error loading tasks')
})
}
</script>

<style>
.widget-content {
max-height: 420px;
}

.suggested {
max-width: 282px;
}
</style>
12 changes: 10 additions & 2 deletions frontend/src/modules/dashboard/pages/dashboard-page.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
<template>
<div class="gap-x-4 px-6 lg:container lg:px-10">
<div>
<app-dashboard-header />
<div class="flex flex-wrap -mx-3 pt-6">
<div class="border-b border-gray-200">
<app-dashboard-header class="pb-4" />
<app-dashboard-task class="!mb-8" />
</div>
<app-dashboard-filters />
<div class="flex flex-wrap -mx-3">
<div class="w-full md:w-2/3 lg:w-3/4 px-3">
<app-dashboard-activities class="mb-6" />
</div>
Expand All @@ -22,10 +26,14 @@ import AppDashboardActivities from '@/modules/dashboard/components/dashboard-act
import AppDashboardMembers from '@/modules/dashboard/components/dashboard-members'
import AppDashboardOrganizations from '@/modules/dashboard/components/dashboard-organizations'
import AppDashboardHeader from '@/modules/dashboard/components/dashboard-header'
import AppDashboardTask from '@/modules/dashboard/components/dashboard-task'
import AppDashboardFilters from '@/modules/dashboard/components/dashboard-filters'

export default {
name: 'AppDashboardPage',
components: {
AppDashboardFilters,
AppDashboardTask,
AppDashboardHeader,
AppDashboardOrganizations,
AppDashboardMembers,
Expand Down
Loading