From ae900e24c033a2aa9bac7be73dfdd9db1f32c149 Mon Sep 17 00:00:00 2001 From: Michal Feher Date: Tue, 14 Feb 2023 11:17:40 +0100 Subject: [PATCH 1/6] auto calculating tracked_time and estimated_time both for master and sub task --- be/app/Models/MasterTask.php | 19 +++++ be/app/Models/SubTask.php | 10 ++- be/app/Models/Timesheet.php | 2 + be/app/Observers/SubTaskObserver.php | 69 +++++++++++++++++++ be/app/Providers/EventServiceProvider.php | 7 +- .../Providers/MasterTaskServiceProvider.php | 30 -------- be/config/app.php | 1 - 7 files changed, 104 insertions(+), 34 deletions(-) create mode 100644 be/app/Observers/SubTaskObserver.php delete mode 100644 be/app/Providers/MasterTaskServiceProvider.php diff --git a/be/app/Models/MasterTask.php b/be/app/Models/MasterTask.php index 54d1b32..234826a 100644 --- a/be/app/Models/MasterTask.php +++ b/be/app/Models/MasterTask.php @@ -64,4 +64,23 @@ class MasterTask extends Model return true; } + + public function recalculateEstimatedTime() + { + $this->estimated_time = $this->subtasks->sum('estimated_time'); + $this->save(); + } + + public function recalculateTrackedTime() + { + $trackedTime = 0.0; + + foreach ( $this->subtasks as $subtask ) { + $trackedTime += $subtask->timesheets->sum('time'); + } + + $this->tracked_time = $trackedTime; + $this->save(); + + } } diff --git a/be/app/Models/SubTask.php b/be/app/Models/SubTask.php index d6fd2f1..bf55046 100644 --- a/be/app/Models/SubTask.php +++ b/be/app/Models/SubTask.php @@ -46,8 +46,11 @@ class SubTask extends Model $item['user_id'] = $item['employee_id']; unset($item['employee_id']); $item['sub_task_id'] = $this->id; - $item['created_at'] = Carbon::now(); - $item['updated_at'] = Carbon::now(); + + if (!$this->id) { + $item['created_at'] = Carbon::now(); + $item['updated_at'] = Carbon::now(); + } return $item; }, $timesheets); @@ -55,6 +58,9 @@ class SubTask extends Model $this->timesheets()->whereNotIn('id', collect($timesheets)->pluck('id')->filter())->delete(); $this->timesheets()->upsert($timesheets, ['id']); + + // kedze updatujem cez upsert, tak sa neaktualizuje updated_at. preto ho musim updatnut manualne + $this->touch(); } public function syncTimeblocks(Array $timeblocks) diff --git a/be/app/Models/Timesheet.php b/be/app/Models/Timesheet.php index 3838437..8df3c00 100644 --- a/be/app/Models/Timesheet.php +++ b/be/app/Models/Timesheet.php @@ -12,6 +12,8 @@ class Timesheet extends Model public $timestamps = true; public $guarded = ['id']; + protected $touches = ['subtask']; + public function subtask() { return $this->belongsTo(SubTask::class, 'sub_task_id'); diff --git a/be/app/Observers/SubTaskObserver.php b/be/app/Observers/SubTaskObserver.php new file mode 100644 index 0000000..45f070a --- /dev/null +++ b/be/app/Observers/SubTaskObserver.php @@ -0,0 +1,69 @@ +tracked_time = $subTask->timesheets->sum('time'); + } + + /** + * Handle the SubTask "updated" event. + * + * @param \App\Models\SubTask $subTask + * @return void + */ + public function updated(SubTask $subTask) + { + $subTask->mastertask->recalculateEstimatedTime(); + $subTask->mastertask->recalculateTrackedTime(); + } + + /** + * Handle the SubTask "deleted" event. + * + * @param \App\Models\SubTask $subTask + * @return void + */ + public function deleted(SubTask $subTask) + { + // + } + + /** + * Handle the SubTask "restored" event. + * + * @param \App\Models\SubTask $subTask + * @return void + */ + public function restored(SubTask $subTask) + { + // + } + + /** + * Handle the SubTask "force deleted" event. + * + * @param \App\Models\SubTask $subTask + * @return void + */ + public function forceDeleted(SubTask $subTask) + { + // + } +} diff --git a/be/app/Providers/EventServiceProvider.php b/be/app/Providers/EventServiceProvider.php index ab8b2cf..20240b3 100644 --- a/be/app/Providers/EventServiceProvider.php +++ b/be/app/Providers/EventServiceProvider.php @@ -2,6 +2,10 @@ namespace App\Providers; +use App\Models\MasterTask; +use App\Models\SubTask; +use App\Observers\MasterTaskObserver; +use App\Observers\SubTaskObserver; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -27,7 +31,8 @@ class EventServiceProvider extends ServiceProvider */ public function boot() { - // + MasterTask::observe( MasterTaskObserver::class); + SubTask::observe( SubTaskObserver::class); } /** diff --git a/be/app/Providers/MasterTaskServiceProvider.php b/be/app/Providers/MasterTaskServiceProvider.php deleted file mode 100644 index 765da5b..0000000 --- a/be/app/Providers/MasterTaskServiceProvider.php +++ /dev/null @@ -1,30 +0,0 @@ - Date: Wed, 15 Feb 2023 09:22:45 +0100 Subject: [PATCH 2/6] GET unscheduled subtasks --- be/app/Http/Controllers/SubTaskController.php | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/be/app/Http/Controllers/SubTaskController.php b/be/app/Http/Controllers/SubTaskController.php index d3fee1f..0450487 100644 --- a/be/app/Http/Controllers/SubTaskController.php +++ b/be/app/Http/Controllers/SubTaskController.php @@ -18,6 +18,11 @@ class SubTaskController extends Controller // get query parameter from request $parameters = $request->query(); + // unscheduled tasks + if (isset($parameters['type']) && $parameters['type'] == 'unscheduled') { + return $this->indexUnscheduled($request); + } + $parameters['user_id'] = ( $parameters['employee_id'] ?? null ); $validator = Validator::make($request->all(), [ @@ -57,6 +62,28 @@ class SubTaskController extends Controller ], 200); } + private function indexUnscheduled(Request $request) { + // get query parameter from request + $parameters = $request->query(); + + $validator = Validator::make( $parameters, [ + 'employee_id' => 'required', + ] ); + + if ( $validator->fails() ) { + return response()->json( $validator->errors(), 400 ); + } + + $parameters['user_id'] = $parameters['employee_id']; + + $subtasks = SubTask::where('user_id', $parameters['user_id'])->whereDoesntHave( 'timeblocks')->get(); + + return response()->json( [ + 'subtasks' => $subtasks + ], 200 ); + } + + public function store($mastertask_id, Request $request) { $validator = Validator::make($request->all(), [ @@ -201,4 +228,5 @@ class SubTaskController extends Controller 'subtask' => $subtask ], 201); } + } From a2a29b529b889958742985a7ea202a98dedeebcc Mon Sep 17 00:00:00 2001 From: Michal Feher Date: Wed, 15 Feb 2023 09:29:08 +0100 Subject: [PATCH 3/6] no message --- be/app/Http/Controllers/SubTaskController.php | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/be/app/Http/Controllers/SubTaskController.php b/be/app/Http/Controllers/SubTaskController.php index 0450487..bfe4418 100644 --- a/be/app/Http/Controllers/SubTaskController.php +++ b/be/app/Http/Controllers/SubTaskController.php @@ -23,6 +23,11 @@ class SubTaskController extends Controller return $this->indexUnscheduled($request); } + // ongoing tasks + if (isset($parameters['type']) && $parameters['type'] == 'ongoing') { + return $this->indexOngoing($request); + } + $parameters['user_id'] = ( $parameters['employee_id'] ?? null ); $validator = Validator::make($request->all(), [ @@ -76,13 +81,35 @@ class SubTaskController extends Controller $parameters['user_id'] = $parameters['employee_id']; - $subtasks = SubTask::where('user_id', $parameters['user_id'])->whereDoesntHave( 'timeblocks')->get(); + $subtasks = SubTask::where('user_id', $parameters['user_id'])->where('is_finished', 0)->whereDoesntHave( 'timeblocks')->get(); return response()->json( [ 'subtasks' => $subtasks ], 200 ); } + private function indexOngoing(Request $request) { + // get query parameter from request + $parameters = $request->query(); + + $validator = Validator::make( $parameters, [ + 'employee_id' => 'required', + ] ); + + if ( $validator->fails() ) { + return response()->json( $validator->errors(), 400 ); + } + + $parameters['user_id'] = $parameters['employee_id']; + + $subtasks = SubTask::where('user_id', $parameters['user_id'])->where('is_finished', 0)->get(); + + return response()->json( [ + 'subtasks' => $subtasks + ], 200 ); + } + + public function store($mastertask_id, Request $request) { From a1a410c54d7d1e3fd97f8df0424510e07d9d9979 Mon Sep 17 00:00:00 2001 From: Michal Feher Date: Wed, 15 Feb 2023 09:36:33 +0100 Subject: [PATCH 4/6] resource instead of model --- be/app/Http/Controllers/SubTaskController.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/be/app/Http/Controllers/SubTaskController.php b/be/app/Http/Controllers/SubTaskController.php index bfe4418..41401b2 100644 --- a/be/app/Http/Controllers/SubTaskController.php +++ b/be/app/Http/Controllers/SubTaskController.php @@ -2,6 +2,8 @@ namespace App\Http\Controllers; +use App\Http\Resources\SubTaskCollection; +use App\Http\Resources\SubTaskResource; use App\Models\Attachment; use App\Models\MasterTask; use App\Models\SubTask; @@ -84,7 +86,7 @@ class SubTaskController extends Controller $subtasks = SubTask::where('user_id', $parameters['user_id'])->where('is_finished', 0)->whereDoesntHave( 'timeblocks')->get(); return response()->json( [ - 'subtasks' => $subtasks + 'subtasks' => SubTaskResource::collection($subtasks) ], 200 ); } @@ -105,7 +107,7 @@ class SubTaskController extends Controller $subtasks = SubTask::where('user_id', $parameters['user_id'])->where('is_finished', 0)->get(); return response()->json( [ - 'subtasks' => $subtasks + 'subtasks' => SubTaskResource::collection($subtasks) ], 200 ); } From 38fb8afd111b96ff9ba07e3d574d37193247f7ea Mon Sep 17 00:00:00 2001 From: Michal Feher Date: Wed, 15 Feb 2023 11:07:11 +0100 Subject: [PATCH 5/6] no message --- be/app/Http/Controllers/SubTaskController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/be/app/Http/Controllers/SubTaskController.php b/be/app/Http/Controllers/SubTaskController.php index 41401b2..d303f29 100644 --- a/be/app/Http/Controllers/SubTaskController.php +++ b/be/app/Http/Controllers/SubTaskController.php @@ -195,7 +195,7 @@ class SubTaskController extends Controller return response()->json([ 'message' => 'Subtask updated successfully', - 'subtask' => $subtask + 'subtask' => new SubTaskResource($subtask) ]); } From 774ca7b6070c22bf7016fbcd3ea5daee6a72c538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Glasn=C3=A1k?= Date: Wed, 15 Feb 2023 15:56:24 +0100 Subject: [PATCH 6/6] [tm@store] Added store --- app/public/mock/v1/auth/info.GET.json | 2 +- app/public/mock/v1/auth/info.POST.json | 2 +- app/public/mock/v1/auth/login.POST.json | 2 +- app/src/plugins/_testing/testing.vue | 62 --- app/src/plugins/app/_config/App.vue | 6 + app/src/plugins/app/_config/main.ts | 28 +- app/src/plugins/app/_config/router.ts | 6 - .../_layout/app/_components/a-sidemenu.vue | 17 +- app/src/plugins/app/_theme/global.sass | 2 + .../plugins/tm@_components/z-files-upload.vue | 1 - app/src/plugins/tm@_store/index.ts | 8 + app/src/plugins/tm@_store/store/index.ts | 203 ++++++++ app/src/plugins/tm@_utils/tm.utils.ts | 6 + .../tm@dashboard/projects/projects.vue | 85 ++-- .../plugins/tm@modal/_components/z-files.vue | 24 + .../tm@modal/blocker/z-blocker-modal.vue | 2 +- ...ption.vue => a-mastertask-description.vue} | 43 +- .../master-task/_mixins/selects.mixin.ts | 3 +- .../z-delete-master-task-modal.vue | 2 +- .../master-task/z-master-task-modal.vue | 47 +- .../_components/a-mastertask-name.vue | 30 ++ .../_components/a-mastertask-status.vue | 29 ++ .../_components/a-task-assigned-employee.vue | 32 ++ .../_components/a-task-description.vue | 17 + .../_components/a-task-planned-time.vue | 28 ++ .../_components/a-task-remaining-time.vue | 29 ++ .../_components/a-task-spent-time.vue | 129 +++++ .../_components/a-task-timeblocks.vue | 148 ++++++ .../task/_components/z-task-readonly-info.vue | 44 ++ .../tm@modal/task/z-complete-task-modal.vue | 2 +- .../tm@modal/task/z-delete-task-modal.vue | 6 +- .../tm@modal/task/z-task-add-time-modal.vue | 2 +- .../plugins/tm@modal/task/z-task-modal.vue | 449 +++--------------- .../tm@modal/team/z-delete-team-modal.vue | 3 +- .../plugins/tm@modal/team/z-team-modal.vue | 22 +- .../_components/z-tasks-search.vue | 47 +- .../_components/a-ongoing-table.vue | 6 +- .../tasks-ongoing/tasks-ongoing.vue | 42 +- .../_components/a-unscheduled-table.vue | 5 +- .../tasks-unscheduled/tasks-unscheduled.vue | 44 +- app/src/plugins/tm@overview/teams/teams.vue | 29 +- .../w/loadable/_components/w-loadable.vue | 3 +- app/src/plugins/w/toast/utils/toast.utils.ts | 10 +- .../plugins/w@custom/axios/models/axios.ts | 2 +- 44 files changed, 989 insertions(+), 720 deletions(-) delete mode 100644 app/src/plugins/_testing/testing.vue create mode 100644 app/src/plugins/tm@_store/index.ts create mode 100644 app/src/plugins/tm@_store/store/index.ts create mode 100644 app/src/plugins/tm@modal/_components/z-files.vue rename app/src/plugins/tm@modal/master-task/_components/{a-task-description.vue => a-mastertask-description.vue} (83%) create mode 100644 app/src/plugins/tm@modal/task/_components/_components/a-mastertask-name.vue create mode 100644 app/src/plugins/tm@modal/task/_components/_components/a-mastertask-status.vue create mode 100644 app/src/plugins/tm@modal/task/_components/_components/a-task-assigned-employee.vue create mode 100644 app/src/plugins/tm@modal/task/_components/_components/a-task-description.vue create mode 100644 app/src/plugins/tm@modal/task/_components/_components/a-task-planned-time.vue create mode 100644 app/src/plugins/tm@modal/task/_components/_components/a-task-remaining-time.vue create mode 100644 app/src/plugins/tm@modal/task/_components/_components/a-task-spent-time.vue create mode 100644 app/src/plugins/tm@modal/task/_components/_components/a-task-timeblocks.vue create mode 100644 app/src/plugins/tm@modal/task/_components/z-task-readonly-info.vue diff --git a/app/public/mock/v1/auth/info.GET.json b/app/public/mock/v1/auth/info.GET.json index cfbcd2d..d40d39a 100644 --- a/app/public/mock/v1/auth/info.GET.json +++ b/app/public/mock/v1/auth/info.GET.json @@ -1,7 +1,7 @@ { "data": { "user": { - "id": 1, + "id": 4, "name": "Rastislav", "surname": "Kuttner", "email": "rastislav.kuttner@jandl.com", diff --git a/app/public/mock/v1/auth/info.POST.json b/app/public/mock/v1/auth/info.POST.json index cfbcd2d..d40d39a 100644 --- a/app/public/mock/v1/auth/info.POST.json +++ b/app/public/mock/v1/auth/info.POST.json @@ -1,7 +1,7 @@ { "data": { "user": { - "id": 1, + "id": 4, "name": "Rastislav", "surname": "Kuttner", "email": "rastislav.kuttner@jandl.com", diff --git a/app/public/mock/v1/auth/login.POST.json b/app/public/mock/v1/auth/login.POST.json index b056ef9..593ac67 100644 --- a/app/public/mock/v1/auth/login.POST.json +++ b/app/public/mock/v1/auth/login.POST.json @@ -2,7 +2,7 @@ "data": { "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvZ2FtaXNmZXJhLndlemVvLmRldlwvY21zXC9hcGlcL3YxXC9hdXRoXC9sb2dpbiIsImlhdCI6MTYxNjQzNTEzMCwiZXhwIjoxNjE2NTIxNTMwLCJuYmYiOjE2MTY0MzUxMzAsImp0aSI6IlJtQmRQNnpDWXJsYWM5d3kiLCJzdWIiOjIsInBydiI6IjFkMDE2ZjgxZmMwNzljMzJkMzY2MDZkYThjOGY4Njk1NzQwYjg0OWQifQ.-a7YeLzozzliAP3UMvqgQEkuHDYG4L04Zdo_sezU9L4", "user": { - "id": 1, + "id": 4, "name": "Rastislav", "surname": "Kuttner", "email": "rastislav.kuttner@jandl.com", diff --git a/app/src/plugins/_testing/testing.vue b/app/src/plugins/_testing/testing.vue deleted file mode 100644 index e1716f5..0000000 --- a/app/src/plugins/_testing/testing.vue +++ /dev/null @@ -1,62 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/plugins/app/_config/App.vue b/app/src/plugins/app/_config/App.vue index 8135608..a83c723 100644 --- a/app/src/plugins/app/_config/App.vue +++ b/app/src/plugins/app/_config/App.vue @@ -42,6 +42,12 @@ export default { ZDeleteMasterTaskModal }, + async created() { + const loader = await this.$loading.show() + await this.$store.dispatch('tm/initialize') + loader?.hide() + }, + mounted() { this.$events.on('show-delete-team-modal', ({team}) => { this.$refs['z-delete-team-modal'].showModal(team) diff --git a/app/src/plugins/app/_config/main.ts b/app/src/plugins/app/_config/main.ts index dc4d824..dfc078d 100644 --- a/app/src/plugins/app/_config/main.ts +++ b/app/src/plugins/app/_config/main.ts @@ -9,6 +9,7 @@ import router from './router' import store from '@/store' import w from '@/plugins/w/w' import axiosCustom from '@/plugins/w@custom/axios' +import tmStore from '@/plugins/tm@_store' import vueCal from '@/plugins/app@vendor/vue-cal' import { Options } from '@/plugins/w/w/types' @@ -51,33 +52,9 @@ const options: Options = { 'POST v1/auth/login': true, 'POST v1/auth/post': true, - // 'GET v1/master-tasks/(id)': true, - // 'GET v1/master-tasks': true, - 'POST v1/master-tasks/(id)': true, - // 'DELETE v1/master-tasks/(id)': true, - // 'POST v1/master-tasks': true, - - // 'GET v1/overview/projects': true, - 'GET v1/overview/tasks': true, 'GET v1/overview/teams': true, - 'GET v1/overview/ongoing-tasks': true, - 'GET v1/overview/unscheduled-tasks': true, - - 'GET v1/tasks/(id)': true, - // 'POST v1/tasks/(id)': true, - 'DELETE v1/tasks/(id)': true, - - // 'GET v1/teams/(id)': true, - // 'POST v1/teams/(id)': true, - // 'DELETE v1/teams/(id)': true, - // 'GET v1/teams': true, - // 'GET v1/teamsx': true, - // 'POST v1/teams': true, - - // 'GET v1/employees': true, + 'GET v1/overview/tasks': true, 'GET v1/search': true, - 'POST v1/tasks': true, - 'POST v1/blockers': true } } @@ -91,6 +68,7 @@ const app = createApp(App) .use(IonicVue) .use(vuetify) .use(vueCal) + .use(tmStore) .use(loader, { color: '#6200EE' }) diff --git a/app/src/plugins/app/_config/router.ts b/app/src/plugins/app/_config/router.ts index 6801cca..9c3b698 100644 --- a/app/src/plugins/app/_config/router.ts +++ b/app/src/plugins/app/_config/router.ts @@ -90,12 +90,6 @@ const routes: Array = [ name: 'Search', meta: { title: 'Search Results' }, component: () => import('@/plugins/lib@search/search/search.vue') - }, - { - path: '/app/testing', - name: 'Testing', - meta: { title: 'Testing' }, - component: () => import('@/plugins/_testing/testing.vue') } ] } diff --git a/app/src/plugins/app/_layout/app/_components/a-sidemenu.vue b/app/src/plugins/app/_layout/app/_components/a-sidemenu.vue index 646cd73..0e7febc 100644 --- a/app/src/plugins/app/_layout/app/_components/a-sidemenu.vue +++ b/app/src/plugins/app/_layout/app/_components/a-sidemenu.vue @@ -15,8 +15,8 @@ :class="{ active: isActive(page.pageName) }" @click="$router.push({ name: page.pageName }); closeMenu()"> {{ page.title }} {{ unscheduledTasksLength }} + v-if="page.pageName == 'Overview - Unscheduled tasks' && unscheduledSubtasks" + class="mb-4 text-center rounded-circle badge">{{ unscheduledSubtasks?.length }} @@ -24,10 +24,11 @@ diff --git a/app/src/plugins/tm@modal/blocker/z-blocker-modal.vue b/app/src/plugins/tm@modal/blocker/z-blocker-modal.vue index 734335a..c74c0b9 100644 --- a/app/src/plugins/tm@modal/blocker/z-blocker-modal.vue +++ b/app/src/plugins/tm@modal/blocker/z-blocker-modal.vue @@ -1,5 +1,5 @@