Create a workflow statistics page from Jira
This commit is contained in:
		
							parent
							
								
									306dd78acb
								
							
						
					
					
						commit
						30cc1c3cac
					
				| 
						 | 
					@ -0,0 +1,172 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Modules\Admin\app\Http\Controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Http\Controllers\Controller;
 | 
				
			||||||
 | 
					use App\Traits\HasFilterRequest;
 | 
				
			||||||
 | 
					use App\Traits\HasOrderByRequest;
 | 
				
			||||||
 | 
					use App\Traits\HasSearchRequest;
 | 
				
			||||||
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					use Illuminate\Http\Response;
 | 
				
			||||||
 | 
					use App\Services\JiraService;
 | 
				
			||||||
 | 
					use GuzzleHttp\Promise\Utils;
 | 
				
			||||||
 | 
					use Maatwebsite\Excel\Facades\Excel;
 | 
				
			||||||
 | 
					use Illuminate\Http\JsonResponse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JiraController extends Controller
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use HasOrderByRequest;
 | 
				
			||||||
 | 
					    use HasFilterRequest;
 | 
				
			||||||
 | 
					    use HasSearchRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $jiraService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(JiraService $jiraService)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->jiraService = $jiraService;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function fetchAllIssues($startAt = 0, $maxResults = 50)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $allIssues = [];
 | 
				
			||||||
 | 
					            $projects = $this->jiraService->getAllProjects();
 | 
				
			||||||
 | 
					            $promises = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach ($projects as $project) {
 | 
				
			||||||
 | 
					                if ($project['key'] !== 'GCT') {
 | 
				
			||||||
 | 
					                    $promises[] = $this->jiraService->getIssuesAsync($project['key'], $startAt, $maxResults)
 | 
				
			||||||
 | 
					                        ->then(function ($response) use ($project) {
 | 
				
			||||||
 | 
					                            $issues = [];
 | 
				
			||||||
 | 
					                            $data = json_decode($response->getBody()->getContents(), true);
 | 
				
			||||||
 | 
					                            foreach ($data['issues'] as $issue) {
 | 
				
			||||||
 | 
					                                $issues[] = [
 | 
				
			||||||
 | 
					                                    'summary' => $issue['fields']['summary'] ?? null,
 | 
				
			||||||
 | 
					                                    'desc' => $issue['fields']['description']['content'][0] ?? null,
 | 
				
			||||||
 | 
					                                    'assignee' => $issue['fields']['assignee']['displayName'] ?? null,
 | 
				
			||||||
 | 
					                                    'status' => $issue['fields']['status']['name'] ?? null,
 | 
				
			||||||
 | 
					                                    'worklogs' => json_encode(array_map(function ($log) {
 | 
				
			||||||
 | 
					                                        return [
 | 
				
			||||||
 | 
					                                            'author' => $log['author']['displayName'] ?? null,
 | 
				
			||||||
 | 
					                                            'timeSpent' => $log['timeSpent'] ?? null,
 | 
				
			||||||
 | 
					                                            'started' => $log['started'] ?? null,
 | 
				
			||||||
 | 
					                                            'updated' => $log['updated'] ?? null,
 | 
				
			||||||
 | 
					                                        ];
 | 
				
			||||||
 | 
					                                    }, $issue['fields']['worklog']['worklogs'] ?? []), JSON_PRETTY_PRINT),
 | 
				
			||||||
 | 
					                                    'originalEstimate' => isset($issue['fields']['timeoriginalestimate']) ? $issue['fields']['timeoriginalestimate'] / 60 / 60 : null,
 | 
				
			||||||
 | 
					                                    'timeSpent' => isset($issue['fields']['timespent']) ? $issue['fields']['timespent'] / 60 / 60 : null
 | 
				
			||||||
 | 
					                                ];
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            return ['project' => $project['name'], 'issues' => $issues];
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $results = Utils::settle($promises)->wait();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach ($results as $result) {
 | 
				
			||||||
 | 
					                if ($result['state'] === 'fulfilled') {
 | 
				
			||||||
 | 
					                    $allIssues[] = $result['value'];
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // Handle the errors if necessary
 | 
				
			||||||
 | 
					                    \Log::error("Error fetching issues: " . $result['reason']->getMessage());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return response()->json($allIssues);
 | 
				
			||||||
 | 
					        } catch (\Exception $e) {
 | 
				
			||||||
 | 
					            return response()->json(['error' => $e->getMessage()], 500);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getAllProject()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $projects = $this->jiraService->getAllProjects();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response()->json([
 | 
				
			||||||
 | 
					            'data' => $projects,
 | 
				
			||||||
 | 
					            'status' => true
 | 
				
			||||||
 | 
					        ], 200);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function fetchIssuesByProject(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $project = ['key'=>$request->key, 'name'=> $request->name];
 | 
				
			||||||
 | 
					        $allIssues = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($project['key'] !== 'GCT') {
 | 
				
			||||||
 | 
					            $startAt = 0;
 | 
				
			||||||
 | 
					            $issues = [];
 | 
				
			||||||
 | 
					            $total = 0;
 | 
				
			||||||
 | 
					            $issueLength = 0;
 | 
				
			||||||
 | 
					            $checked = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // while ($checked) {
 | 
				
			||||||
 | 
					                $response = $this->jiraService->getIssues($project['key'], $startAt);
 | 
				
			||||||
 | 
					                $total = $response['total'];
 | 
				
			||||||
 | 
					                $issueLength = count($response['issues']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                foreach ($response['issues'] as $issue) {
 | 
				
			||||||
 | 
					                    $issues[] = [
 | 
				
			||||||
 | 
					                        'summary' => $issue['fields']['summary'] ?? null,
 | 
				
			||||||
 | 
					                        'desc' => $issue['fields']['description']['content'][0] ?? null,
 | 
				
			||||||
 | 
					                        'assignee' => $issue['fields']['assignee']['displayName'] ?? null,
 | 
				
			||||||
 | 
					                        'status' => $issue['fields']['status']['name'] ?? null,
 | 
				
			||||||
 | 
					                        'worklogs' => json_encode(array_map(function ($log) {
 | 
				
			||||||
 | 
					                            return [
 | 
				
			||||||
 | 
					                                'author' => $log['author']['displayName'] ?? null,
 | 
				
			||||||
 | 
					                                'timeSpent' => $log['timeSpent'] ?? null,
 | 
				
			||||||
 | 
					                                'started' => $log['started'] ?? null,
 | 
				
			||||||
 | 
					                                'updated' => $log['updated'] ?? null,
 | 
				
			||||||
 | 
					                            ];
 | 
				
			||||||
 | 
					                        }, $issue['fields']['worklog']['worklogs'] ?? []), JSON_PRETTY_PRINT),
 | 
				
			||||||
 | 
					                        'originalEstimate' => isset($issue['fields']['timeoriginalestimate']) ? $issue['fields']['timeoriginalestimate'] / 60 / 60 : null,
 | 
				
			||||||
 | 
					                        'timeSpent' => isset($issue['fields']['timespent']) ? $issue['fields']['timespent'] / 60 / 60 : null
 | 
				
			||||||
 | 
					                    ];
 | 
				
			||||||
 | 
					                // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // if (($startAt + $issueLength >= $total && $total !== 0) || $total === 0) {
 | 
				
			||||||
 | 
					                //     $checked = false;
 | 
				
			||||||
 | 
					                // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // $startAt += $issueLength;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $allIssues[] = [
 | 
				
			||||||
 | 
					                'project' => $project['name'],
 | 
				
			||||||
 | 
					                'issues' => $issues
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return response()->json([
 | 
				
			||||||
 | 
					                'data' => $allIssues,
 | 
				
			||||||
 | 
					                'status' => true
 | 
				
			||||||
 | 
					            ], 200);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return response()->json([
 | 
				
			||||||
 | 
					            'data' => $allIssues,
 | 
				
			||||||
 | 
					            'status' => false
 | 
				
			||||||
 | 
					        ], 500);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function exportToExcel()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $allIssues = $this->fetchAllIssues()->original;
 | 
				
			||||||
 | 
					        $fileName = 'allIssues.xlsx';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Excel::store(new \App\Exports\IssuesExport($allIssues), $fileName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response()->download(storage_path("app/{$fileName}"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getAllUserWorkLogs(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $workLogs = $this->jiraService->getAllUserWorkLogs($request->startDate, $request->endDate);
 | 
				
			||||||
 | 
					            return response()->json([
 | 
				
			||||||
 | 
					                'data' => $workLogs,
 | 
				
			||||||
 | 
					                'status' => true
 | 
				
			||||||
 | 
					            ], 200);
 | 
				
			||||||
 | 
					        } catch (\Exception $e) {
 | 
				
			||||||
 | 
					            return response()->json(['error' => $e->getMessage()], 500);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -7,10 +7,7 @@ use App\Traits\HasFilterRequest;
 | 
				
			||||||
use App\Traits\HasOrderByRequest;
 | 
					use App\Traits\HasOrderByRequest;
 | 
				
			||||||
use App\Traits\HasSearchRequest;
 | 
					use App\Traits\HasSearchRequest;
 | 
				
			||||||
use Illuminate\Http\Request;
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
use Illuminate\Http\Response;
 | 
					 | 
				
			||||||
use Modules\Admin\app\Http\Requests\DiscountRequest;
 | 
					 | 
				
			||||||
use Modules\Admin\app\Models\Admin;
 | 
					use Modules\Admin\app\Models\Admin;
 | 
				
			||||||
use Modules\Admin\app\Models\Discount;
 | 
					 | 
				
			||||||
use Modules\Admin\app\Models\Tracking;
 | 
					use Modules\Admin\app\Models\Tracking;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TrackingController extends Controller
 | 
					class TrackingController extends Controller
 | 
				
			||||||
| 
						 | 
					@ -148,39 +145,4 @@ class TrackingController extends Controller
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Delete multiple discounts
 | 
					 | 
				
			||||||
    public function deletes(DiscountRequest $request)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $discounts = $request->get('discounts');
 | 
					 | 
				
			||||||
        $ids = collect($discounts)->pluck('id');
 | 
					 | 
				
			||||||
        Discount::whereIn('id', $ids)->delete();
 | 
					 | 
				
			||||||
        return response()->json([
 | 
					 | 
				
			||||||
            'data' => $ids,
 | 
					 | 
				
			||||||
            'status' => true
 | 
					 | 
				
			||||||
        ]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Update multiple discounts
 | 
					 | 
				
			||||||
    public function updates(DiscountRequest $request)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $discounts = $request->get('discounts');
 | 
					 | 
				
			||||||
        $ids = collect($discounts)->pluck('id');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        foreach ($discounts as $discountRequest) {
 | 
					 | 
				
			||||||
            // convert to object|array to array
 | 
					 | 
				
			||||||
            $discountRequest = collect($discountRequest)->toArray();
 | 
					 | 
				
			||||||
            // handle array
 | 
					 | 
				
			||||||
            $discount = Discount::find($discountRequest['id']);
 | 
					 | 
				
			||||||
            if ($discount) {
 | 
					 | 
				
			||||||
                // exclude id field
 | 
					 | 
				
			||||||
                unset($discount['id']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $discount->update($discountRequest);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return response()->json([
 | 
					 | 
				
			||||||
            'data' => Discount::whereIn('id', $ids)->get(),
 | 
					 | 
				
			||||||
            'status' => true
 | 
					 | 
				
			||||||
        ]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ use Modules\Admin\app\Http\Controllers\CustomThemeController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\DashboardController;
 | 
					use Modules\Admin\app\Http\Controllers\DashboardController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\DiscountController;
 | 
					use Modules\Admin\app\Http\Controllers\DiscountController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\DiscountTypeController;
 | 
					use Modules\Admin\app\Http\Controllers\DiscountTypeController;
 | 
				
			||||||
 | 
					use Modules\Admin\app\Http\Controllers\JiraController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\OrderController;
 | 
					use Modules\Admin\app\Http\Controllers\OrderController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\PackageController;
 | 
					use Modules\Admin\app\Http\Controllers\PackageController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\SNCheckController;
 | 
					use Modules\Admin\app\Http\Controllers\SNCheckController;
 | 
				
			||||||
| 
						 | 
					@ -142,6 +143,16 @@ Route::middleware('api')
 | 
				
			||||||
                Route::get('/statistics-search-sn-by-month', [DashboardController::class, 'statisticSearchSNByMonth']);
 | 
					                Route::get('/statistics-search-sn-by-month', [DashboardController::class, 'statisticSearchSNByMonth']);
 | 
				
			||||||
                Route::get('/statistics-revenues-by-month', [DashboardController::class, 'statisticRevenuesByMonth']);
 | 
					                Route::get('/statistics-revenues-by-month', [DashboardController::class, 'statisticRevenuesByMonth']);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					            Route::group([
 | 
				
			||||||
 | 
					                'prefix' => 'jira',
 | 
				
			||||||
 | 
					            ], function () {
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                Route::get('/fetch-issues', [JiraController::class, 'fetchAllIssues']);
 | 
				
			||||||
 | 
					                Route::get('/export-issues', [JiraController::class, 'exportToExcel']);
 | 
				
			||||||
 | 
					                Route::get('/all-project', [JiraController::class, 'getAllProject']);
 | 
				
			||||||
 | 
					                Route::get('/all-issue-by-project', [JiraController::class, 'fetchIssuesByProject']);
 | 
				
			||||||
 | 
					                Route::get('/worklogs', [JiraController::class, 'getAllUserWorkLogs']);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -152,4 +163,5 @@ Route::middleware('api')
 | 
				
			||||||
        Route::post('/scan-create', [TrackingController::class, 'create']);
 | 
					        Route::post('/scan-create', [TrackingController::class, 'create']);
 | 
				
			||||||
        Route::get('/delete', [TrackingController::class, 'delete']);
 | 
					        Route::get('/delete', [TrackingController::class, 'delete']);
 | 
				
			||||||
        // Route::get('/clear-cache', [SettingController::class, 'clearCache']);
 | 
					        // Route::get('/clear-cache', [SettingController::class, 'clearCache']);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Exports;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Maatwebsite\Excel\Concerns\Exportable;
 | 
				
			||||||
 | 
					use Maatwebsite\Excel\Concerns\WithMultipleSheets;
 | 
				
			||||||
 | 
					class IssuesExport implements WithMultipleSheets {
 | 
				
			||||||
 | 
					    use Exportable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(array $data)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->data = $data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function sheets(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $sheets = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($this->data as $projectData) {
 | 
				
			||||||
 | 
					            $sheets[] = new ProjectSheet($projectData);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $sheets;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Exports;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Maatwebsite\Excel\Concerns\FromArray;
 | 
				
			||||||
 | 
					use Maatwebsite\Excel\Concerns\WithHeadings;
 | 
				
			||||||
 | 
					use Maatwebsite\Excel\Concerns\WithTitle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ProjectSheet implements FromArray, WithHeadings, WithTitle
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected $projectData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(array $projectData)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->projectData = $projectData;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function array(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->projectData['issues'];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function headings(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return ['summary', 'desc', 'assignee', 'status', 'worklogs', 'originalEstimate', 'timeSpent'];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function title(): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Return the project name or any other string based on $projectData
 | 
				
			||||||
 | 
					        return $this->projectData['project'];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,203 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use GuzzleHttp\Client;
 | 
				
			||||||
 | 
					use GuzzleHttp\Promise\Utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JiraService
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected $client;
 | 
				
			||||||
 | 
					    protected $baseUrl;
 | 
				
			||||||
 | 
					    protected $authHeader;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->baseUrl = env('JIRA_BASE_URL');
 | 
				
			||||||
 | 
					        $this->authHeader = 'Basic ' . base64_encode(env('JIRA_USERNAME') . ':' . env('JIRA_API_TOKEN'));
 | 
				
			||||||
 | 
					        $this->client = new Client([
 | 
				
			||||||
 | 
					            'base_uri' => $this->baseUrl,
 | 
				
			||||||
 | 
					            'headers' => [
 | 
				
			||||||
 | 
					                'Authorization' => $this->authHeader,
 | 
				
			||||||
 | 
					                'Accept' => 'application/json',
 | 
				
			||||||
 | 
					                'Content-Type' => 'application/json'
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getAllProjects()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $response = $this->client->get('/rest/api/3/project');
 | 
				
			||||||
 | 
					        return json_decode($response->getBody()->getContents(), true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getIssuesAsync($projectKey, $startAt = 0, $maxResults = 50)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $body = [
 | 
				
			||||||
 | 
					            'expand' => ['names', 'schema', 'operations'],
 | 
				
			||||||
 | 
					            'fields' => ['summary', 'status', 'description', 'timeoriginalestimate', 'timespent', 'worklog', 'assignee'],
 | 
				
			||||||
 | 
					            'jql' => "project = '{$projectKey}' ORDER BY created DESC",
 | 
				
			||||||
 | 
					            'maxResults' => $maxResults,
 | 
				
			||||||
 | 
					            'startAt' => $startAt
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this->client->postAsync($this->baseUrl . '/rest/api/3/search', [
 | 
				
			||||||
 | 
					            'body' => json_encode($body),
 | 
				
			||||||
 | 
					            'headers' => [
 | 
				
			||||||
 | 
					                'Authorization' => $this->authHeader,
 | 
				
			||||||
 | 
					                'Accept' => 'application/json',
 | 
				
			||||||
 | 
					                'Content-Type' => 'application/json'
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function getIssues($projectKey, $startAt = 0)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $body = [
 | 
				
			||||||
 | 
					            'expand' => ['names', 'schema', 'operations'],
 | 
				
			||||||
 | 
					            'fields' => ['summary', 'status', 'description', 'timeoriginalestimate', 'timespent', 'worklog', 'assignee'],
 | 
				
			||||||
 | 
					            'jql' => "project = '{$projectKey}' ORDER BY created DESC",
 | 
				
			||||||
 | 
					            'maxResults' => 100,
 | 
				
			||||||
 | 
					            'startAt' => $startAt
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response = $this->client->post('/rest/api/3/search', [
 | 
				
			||||||
 | 
					            'body' => json_encode($body)
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return json_decode($response->getBody()->getContents(), true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getAllUsers()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $response = $this->client->get('/rest/api/3/users/search', [
 | 
				
			||||||
 | 
					            'headers' => [
 | 
				
			||||||
 | 
					                'Authorization' => $this->authHeader,
 | 
				
			||||||
 | 
					                'Accept' => 'application/json'
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return json_decode($response->getBody()->getContents(), true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getUserWorkLogs($accountId, $startDate, $endDate)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $body = [
 | 
				
			||||||
 | 
					            'jql' => "worklogAuthor = '{$accountId}'AND worklogDate >= '{$startDate}' AND worklogDate <= '{$endDate}'",
 | 
				
			||||||
 | 
					            'fields' => ['summary', 'status', 'timeoriginalestimate', 'timespent', 'project'],
 | 
				
			||||||
 | 
					            'maxResults' => 50
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response = $this->client->post('/rest/api/3/search', [
 | 
				
			||||||
 | 
					            'body' => json_encode($body),
 | 
				
			||||||
 | 
					            'headers' => [
 | 
				
			||||||
 | 
					                'Authorization' => $this->authHeader,
 | 
				
			||||||
 | 
					                'Accept' => 'application/json',
 | 
				
			||||||
 | 
					                'Content-Type' => 'application/json'
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $data_response = json_decode($response->getBody()->getContents(), true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($data_response['total'] == 0) {
 | 
				
			||||||
 | 
					            return $data_response;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $promises = [];
 | 
				
			||||||
 | 
					        foreach ($data_response['issues'] as $index => $issue) {
 | 
				
			||||||
 | 
					            $issueId = $issue['id'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Get the initial worklog data to determine total number of worklogs
 | 
				
			||||||
 | 
					            $promises[$index] = $this->client->getAsync("/rest/api/3/issue/{$issueId}/worklog", [
 | 
				
			||||||
 | 
					                'query' => [
 | 
				
			||||||
 | 
					                    'startAt' => 0,
 | 
				
			||||||
 | 
					                    'maxResults' => 1
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ])->then(function ($checkApiResponse) use ($issueId, $index) {
 | 
				
			||||||
 | 
					                $checkApi = json_decode($checkApiResponse->getBody()->getContents(), true);
 | 
				
			||||||
 | 
					                $maxResults = 50;
 | 
				
			||||||
 | 
					                $totalWorklogs = $checkApi['total'];
 | 
				
			||||||
 | 
					                return $this->client->getAsync("/rest/api/3/issue/{$issueId}/worklog", [
 | 
				
			||||||
 | 
					                    'query' => [
 | 
				
			||||||
 | 
					                        'startAt' => $totalWorklogs - $maxResults,
 | 
				
			||||||
 | 
					                        'maxResults' => $totalWorklogs
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
 | 
					                ])->then(function ($worklogResponse) use ($index) {
 | 
				
			||||||
 | 
					                    return [
 | 
				
			||||||
 | 
					                        'index' => $index,
 | 
				
			||||||
 | 
					                        'worklogs' => json_decode($worklogResponse->getBody()->getContents(), true)
 | 
				
			||||||
 | 
					                    ];
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Wait for all promises to complete
 | 
				
			||||||
 | 
					        $results = Utils::settle($promises)->wait();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Attach worklogs to issues
 | 
				
			||||||
 | 
					        foreach ($results as $result) {
 | 
				
			||||||
 | 
					            if ($result['state'] === 'fulfilled') {
 | 
				
			||||||
 | 
					                $data_response['issues'][$result['value']['index']]["fields"]['worklog'] = $result['value']['worklogs'];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $data_response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // public function getUserWorkLogs($accountId, $startDate, $endDate)
 | 
				
			||||||
 | 
					    // {
 | 
				
			||||||
 | 
					    //     $body = [
 | 
				
			||||||
 | 
					    //         'jql' => "worklogAuthor = '{$accountId}'AND worklogDate >= '{$startDate}' AND worklogDate <= '{$endDate}'",
 | 
				
			||||||
 | 
					    //         'fields' => ['summary', 'status', 'timeoriginalestimate', 'timespent', 'project'],
 | 
				
			||||||
 | 
					    //         'maxResults' => 50
 | 
				
			||||||
 | 
					    //     ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     $response = $this->client->post('/rest/api/3/search', [
 | 
				
			||||||
 | 
					    //         'body' => json_encode($body),
 | 
				
			||||||
 | 
					    //         'headers' => [
 | 
				
			||||||
 | 
					    //             'Authorization' => $this->authHeader,
 | 
				
			||||||
 | 
					    //             'Accept' => 'application/json',
 | 
				
			||||||
 | 
					    //             'Content-Type' => 'application/json'
 | 
				
			||||||
 | 
					    //         ]
 | 
				
			||||||
 | 
					    //     ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     $data_response = json_decode($response->getBody()->getContents(), true);
 | 
				
			||||||
 | 
					    //     // $allRespones = [];
 | 
				
			||||||
 | 
					    //     if ($data_response['total'] != 0) {
 | 
				
			||||||
 | 
					    //         foreach ($data_response['issues']  as $index => $issue) {
 | 
				
			||||||
 | 
					    //             $maxResults = 10;
 | 
				
			||||||
 | 
					    //             $check_api = $this->client->get("/rest/api/3/issue/{$issue['id']}/worklog", [
 | 
				
			||||||
 | 
					    //                 'query' => [
 | 
				
			||||||
 | 
					    //                     'startAt' => 0,
 | 
				
			||||||
 | 
					    //                     'maxResults' => 1
 | 
				
			||||||
 | 
					    //                 ]
 | 
				
			||||||
 | 
					    //             ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //             $check_api = json_decode($check_api->getBody()->getContents(), true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //                 $response = $this->client->get("/rest/api/3/issue/{$issue['id']}/worklog", [
 | 
				
			||||||
 | 
					    //                     'query' => [
 | 
				
			||||||
 | 
					    //                         'startAt' => $check_api['total'] - $maxResults,
 | 
				
			||||||
 | 
					    //                         'maxResults' => $check_api['total']
 | 
				
			||||||
 | 
					    //                     ]
 | 
				
			||||||
 | 
					    //                 ]);
 | 
				
			||||||
 | 
					    //             $data_response['issues'][$index]["fields"]['worklogs'] = json_decode($response->getBody()->getContents(), true);
 | 
				
			||||||
 | 
					    //         }
 | 
				
			||||||
 | 
					    //     }
 | 
				
			||||||
 | 
					    //     return $data_response;
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getAllUserWorkLogs($startDate, $endDate)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $users = $this->getAllUsers();
 | 
				
			||||||
 | 
					        $workLogs = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($users as $user) {
 | 
				
			||||||
 | 
					            $userWorkLogs = $this->getUserWorkLogs($user['accountId'], $startDate, $endDate);
 | 
				
			||||||
 | 
					            $workLogs[] = ['username' => $user['displayName'], 'information' => $userWorkLogs];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $workLogs;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,7 @@
 | 
				
			||||||
        "laravel/sanctum": "^3.2",
 | 
					        "laravel/sanctum": "^3.2",
 | 
				
			||||||
        "laravel/tinker": "^2.8",
 | 
					        "laravel/tinker": "^2.8",
 | 
				
			||||||
        "laravel/ui": "^4.3",
 | 
					        "laravel/ui": "^4.3",
 | 
				
			||||||
 | 
					        "maatwebsite/excel": "^3.1",
 | 
				
			||||||
        "nwidart/laravel-modules": "^10.0",
 | 
					        "nwidart/laravel-modules": "^10.0",
 | 
				
			||||||
        "pion/laravel-chunk-upload": "^1.5",
 | 
					        "pion/laravel-chunk-upload": "^1.5",
 | 
				
			||||||
        "predis/predis": "^2.2",
 | 
					        "predis/predis": "^2.2",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@
 | 
				
			||||||
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
 | 
					        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
 | 
				
			||||||
        "This file is @generated automatically"
 | 
					        "This file is @generated automatically"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "content-hash": "879d4b76d94550aedcc3fa47d3db8f96",
 | 
					    "content-hash": "4d9f50111be5d1e2be1581ccf00970b6",
 | 
				
			||||||
    "packages": [
 | 
					    "packages": [
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "barryvdh/laravel-debugbar",
 | 
					            "name": "barryvdh/laravel-debugbar",
 | 
				
			||||||
| 
						 | 
					@ -219,6 +219,87 @@
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "time": "2023-12-11T17:09:12+00:00"
 | 
					            "time": "2023-12-11T17:09:12+00:00"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "composer/semver",
 | 
				
			||||||
 | 
					            "version": "3.4.0",
 | 
				
			||||||
 | 
					            "source": {
 | 
				
			||||||
 | 
					                "type": "git",
 | 
				
			||||||
 | 
					                "url": "https://github.com/composer/semver.git",
 | 
				
			||||||
 | 
					                "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "dist": {
 | 
				
			||||||
 | 
					                "type": "zip",
 | 
				
			||||||
 | 
					                "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32",
 | 
				
			||||||
 | 
					                "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32",
 | 
				
			||||||
 | 
					                "shasum": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require": {
 | 
				
			||||||
 | 
					                "php": "^5.3.2 || ^7.0 || ^8.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require-dev": {
 | 
				
			||||||
 | 
					                "phpstan/phpstan": "^1.4",
 | 
				
			||||||
 | 
					                "symfony/phpunit-bridge": "^4.2 || ^5"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "type": "library",
 | 
				
			||||||
 | 
					            "extra": {
 | 
				
			||||||
 | 
					                "branch-alias": {
 | 
				
			||||||
 | 
					                    "dev-main": "3.x-dev"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "autoload": {
 | 
				
			||||||
 | 
					                "psr-4": {
 | 
				
			||||||
 | 
					                    "Composer\\Semver\\": "src"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "notification-url": "https://packagist.org/downloads/",
 | 
				
			||||||
 | 
					            "license": [
 | 
				
			||||||
 | 
					                "MIT"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "authors": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Nils Adermann",
 | 
				
			||||||
 | 
					                    "email": "naderman@naderman.de",
 | 
				
			||||||
 | 
					                    "homepage": "http://www.naderman.de"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Jordi Boggiano",
 | 
				
			||||||
 | 
					                    "email": "j.boggiano@seld.be",
 | 
				
			||||||
 | 
					                    "homepage": "http://seld.be"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Rob Bast",
 | 
				
			||||||
 | 
					                    "email": "rob.bast@gmail.com",
 | 
				
			||||||
 | 
					                    "homepage": "http://robbast.nl"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "description": "Semver library that offers utilities, version constraint parsing and validation.",
 | 
				
			||||||
 | 
					            "keywords": [
 | 
				
			||||||
 | 
					                "semantic",
 | 
				
			||||||
 | 
					                "semver",
 | 
				
			||||||
 | 
					                "validation",
 | 
				
			||||||
 | 
					                "versioning"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "support": {
 | 
				
			||||||
 | 
					                "irc": "ircs://irc.libera.chat:6697/composer",
 | 
				
			||||||
 | 
					                "issues": "https://github.com/composer/semver/issues",
 | 
				
			||||||
 | 
					                "source": "https://github.com/composer/semver/tree/3.4.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "funding": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "url": "https://packagist.com",
 | 
				
			||||||
 | 
					                    "type": "custom"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "url": "https://github.com/composer",
 | 
				
			||||||
 | 
					                    "type": "github"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
 | 
				
			||||||
 | 
					                    "type": "tidelift"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "time": "2023-08-31T09:50:34+00:00"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "dflydev/dot-access-data",
 | 
					            "name": "dflydev/dot-access-data",
 | 
				
			||||||
            "version": "v3.0.2",
 | 
					            "version": "v3.0.2",
 | 
				
			||||||
| 
						 | 
					@ -642,6 +723,67 @@
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "time": "2023-10-06T06:47:41+00:00"
 | 
					            "time": "2023-10-06T06:47:41+00:00"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "ezyang/htmlpurifier",
 | 
				
			||||||
 | 
					            "version": "v4.17.0",
 | 
				
			||||||
 | 
					            "source": {
 | 
				
			||||||
 | 
					                "type": "git",
 | 
				
			||||||
 | 
					                "url": "https://github.com/ezyang/htmlpurifier.git",
 | 
				
			||||||
 | 
					                "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "dist": {
 | 
				
			||||||
 | 
					                "type": "zip",
 | 
				
			||||||
 | 
					                "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/bbc513d79acf6691fa9cf10f192c90dd2957f18c",
 | 
				
			||||||
 | 
					                "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c",
 | 
				
			||||||
 | 
					                "shasum": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require": {
 | 
				
			||||||
 | 
					                "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require-dev": {
 | 
				
			||||||
 | 
					                "cerdic/css-tidy": "^1.7 || ^2.0",
 | 
				
			||||||
 | 
					                "simpletest/simpletest": "dev-master"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "suggest": {
 | 
				
			||||||
 | 
					                "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
 | 
				
			||||||
 | 
					                "ext-bcmath": "Used for unit conversion and imagecrash protection",
 | 
				
			||||||
 | 
					                "ext-iconv": "Converts text to and from non-UTF-8 encodings",
 | 
				
			||||||
 | 
					                "ext-tidy": "Used for pretty-printing HTML"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "type": "library",
 | 
				
			||||||
 | 
					            "autoload": {
 | 
				
			||||||
 | 
					                "files": [
 | 
				
			||||||
 | 
					                    "library/HTMLPurifier.composer.php"
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                "psr-0": {
 | 
				
			||||||
 | 
					                    "HTMLPurifier": "library/"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "exclude-from-classmap": [
 | 
				
			||||||
 | 
					                    "/library/HTMLPurifier/Language/"
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "notification-url": "https://packagist.org/downloads/",
 | 
				
			||||||
 | 
					            "license": [
 | 
				
			||||||
 | 
					                "LGPL-2.1-or-later"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "authors": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Edward Z. Yang",
 | 
				
			||||||
 | 
					                    "email": "admin@htmlpurifier.org",
 | 
				
			||||||
 | 
					                    "homepage": "http://ezyang.com"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "description": "Standards compliant HTML filter written in PHP",
 | 
				
			||||||
 | 
					            "homepage": "http://htmlpurifier.org/",
 | 
				
			||||||
 | 
					            "keywords": [
 | 
				
			||||||
 | 
					                "html"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "support": {
 | 
				
			||||||
 | 
					                "issues": "https://github.com/ezyang/htmlpurifier/issues",
 | 
				
			||||||
 | 
					                "source": "https://github.com/ezyang/htmlpurifier/tree/v4.17.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "time": "2023-11-17T15:01:25+00:00"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "fruitcake/php-cors",
 | 
					            "name": "fruitcake/php-cors",
 | 
				
			||||||
            "version": "v1.3.0",
 | 
					            "version": "v1.3.0",
 | 
				
			||||||
| 
						 | 
					@ -2237,6 +2379,275 @@
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "time": "2024-01-28T23:22:08+00:00"
 | 
					            "time": "2024-01-28T23:22:08+00:00"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "maatwebsite/excel",
 | 
				
			||||||
 | 
					            "version": "3.1.55",
 | 
				
			||||||
 | 
					            "source": {
 | 
				
			||||||
 | 
					                "type": "git",
 | 
				
			||||||
 | 
					                "url": "https://github.com/SpartnerNL/Laravel-Excel.git",
 | 
				
			||||||
 | 
					                "reference": "6d9d791dcdb01a9b6fd6f48d46f0d5fff86e6260"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "dist": {
 | 
				
			||||||
 | 
					                "type": "zip",
 | 
				
			||||||
 | 
					                "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/6d9d791dcdb01a9b6fd6f48d46f0d5fff86e6260",
 | 
				
			||||||
 | 
					                "reference": "6d9d791dcdb01a9b6fd6f48d46f0d5fff86e6260",
 | 
				
			||||||
 | 
					                "shasum": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require": {
 | 
				
			||||||
 | 
					                "composer/semver": "^3.3",
 | 
				
			||||||
 | 
					                "ext-json": "*",
 | 
				
			||||||
 | 
					                "illuminate/support": "5.8.*||^6.0||^7.0||^8.0||^9.0||^10.0||^11.0",
 | 
				
			||||||
 | 
					                "php": "^7.0||^8.0",
 | 
				
			||||||
 | 
					                "phpoffice/phpspreadsheet": "^1.18",
 | 
				
			||||||
 | 
					                "psr/simple-cache": "^1.0||^2.0||^3.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require-dev": {
 | 
				
			||||||
 | 
					                "laravel/scout": "^7.0||^8.0||^9.0||^10.0",
 | 
				
			||||||
 | 
					                "orchestra/testbench": "^6.0||^7.0||^8.0||^9.0",
 | 
				
			||||||
 | 
					                "predis/predis": "^1.1"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "type": "library",
 | 
				
			||||||
 | 
					            "extra": {
 | 
				
			||||||
 | 
					                "laravel": {
 | 
				
			||||||
 | 
					                    "providers": [
 | 
				
			||||||
 | 
					                        "Maatwebsite\\Excel\\ExcelServiceProvider"
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    "aliases": {
 | 
				
			||||||
 | 
					                        "Excel": "Maatwebsite\\Excel\\Facades\\Excel"
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "autoload": {
 | 
				
			||||||
 | 
					                "psr-4": {
 | 
				
			||||||
 | 
					                    "Maatwebsite\\Excel\\": "src/"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "notification-url": "https://packagist.org/downloads/",
 | 
				
			||||||
 | 
					            "license": [
 | 
				
			||||||
 | 
					                "MIT"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "authors": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Patrick Brouwers",
 | 
				
			||||||
 | 
					                    "email": "patrick@spartner.nl"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "description": "Supercharged Excel exports and imports in Laravel",
 | 
				
			||||||
 | 
					            "keywords": [
 | 
				
			||||||
 | 
					                "PHPExcel",
 | 
				
			||||||
 | 
					                "batch",
 | 
				
			||||||
 | 
					                "csv",
 | 
				
			||||||
 | 
					                "excel",
 | 
				
			||||||
 | 
					                "export",
 | 
				
			||||||
 | 
					                "import",
 | 
				
			||||||
 | 
					                "laravel",
 | 
				
			||||||
 | 
					                "php",
 | 
				
			||||||
 | 
					                "phpspreadsheet"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "support": {
 | 
				
			||||||
 | 
					                "issues": "https://github.com/SpartnerNL/Laravel-Excel/issues",
 | 
				
			||||||
 | 
					                "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.55"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "funding": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "url": "https://laravel-excel.com/commercial-support",
 | 
				
			||||||
 | 
					                    "type": "custom"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "url": "https://github.com/patrickbrouwers",
 | 
				
			||||||
 | 
					                    "type": "github"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "time": "2024-02-20T08:27:10+00:00"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "maennchen/zipstream-php",
 | 
				
			||||||
 | 
					            "version": "3.1.0",
 | 
				
			||||||
 | 
					            "source": {
 | 
				
			||||||
 | 
					                "type": "git",
 | 
				
			||||||
 | 
					                "url": "https://github.com/maennchen/ZipStream-PHP.git",
 | 
				
			||||||
 | 
					                "reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "dist": {
 | 
				
			||||||
 | 
					                "type": "zip",
 | 
				
			||||||
 | 
					                "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/b8174494eda667f7d13876b4a7bfef0f62a7c0d1",
 | 
				
			||||||
 | 
					                "reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1",
 | 
				
			||||||
 | 
					                "shasum": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require": {
 | 
				
			||||||
 | 
					                "ext-mbstring": "*",
 | 
				
			||||||
 | 
					                "ext-zlib": "*",
 | 
				
			||||||
 | 
					                "php-64bit": "^8.1"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require-dev": {
 | 
				
			||||||
 | 
					                "ext-zip": "*",
 | 
				
			||||||
 | 
					                "friendsofphp/php-cs-fixer": "^3.16",
 | 
				
			||||||
 | 
					                "guzzlehttp/guzzle": "^7.5",
 | 
				
			||||||
 | 
					                "mikey179/vfsstream": "^1.6",
 | 
				
			||||||
 | 
					                "php-coveralls/php-coveralls": "^2.5",
 | 
				
			||||||
 | 
					                "phpunit/phpunit": "^10.0",
 | 
				
			||||||
 | 
					                "vimeo/psalm": "^5.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "suggest": {
 | 
				
			||||||
 | 
					                "guzzlehttp/psr7": "^2.4",
 | 
				
			||||||
 | 
					                "psr/http-message": "^2.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "type": "library",
 | 
				
			||||||
 | 
					            "autoload": {
 | 
				
			||||||
 | 
					                "psr-4": {
 | 
				
			||||||
 | 
					                    "ZipStream\\": "src/"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "notification-url": "https://packagist.org/downloads/",
 | 
				
			||||||
 | 
					            "license": [
 | 
				
			||||||
 | 
					                "MIT"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "authors": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Paul Duncan",
 | 
				
			||||||
 | 
					                    "email": "pabs@pablotron.org"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Jonatan Männchen",
 | 
				
			||||||
 | 
					                    "email": "jonatan@maennchen.ch"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Jesse Donat",
 | 
				
			||||||
 | 
					                    "email": "donatj@gmail.com"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "András Kolesár",
 | 
				
			||||||
 | 
					                    "email": "kolesar@kolesar.hu"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
 | 
				
			||||||
 | 
					            "keywords": [
 | 
				
			||||||
 | 
					                "stream",
 | 
				
			||||||
 | 
					                "zip"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "support": {
 | 
				
			||||||
 | 
					                "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
 | 
				
			||||||
 | 
					                "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "funding": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "url": "https://github.com/maennchen",
 | 
				
			||||||
 | 
					                    "type": "github"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "url": "https://opencollective.com/zipstream",
 | 
				
			||||||
 | 
					                    "type": "open_collective"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "time": "2023-06-21T14:59:35+00:00"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "markbaker/complex",
 | 
				
			||||||
 | 
					            "version": "3.0.2",
 | 
				
			||||||
 | 
					            "source": {
 | 
				
			||||||
 | 
					                "type": "git",
 | 
				
			||||||
 | 
					                "url": "https://github.com/MarkBaker/PHPComplex.git",
 | 
				
			||||||
 | 
					                "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "dist": {
 | 
				
			||||||
 | 
					                "type": "zip",
 | 
				
			||||||
 | 
					                "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
 | 
				
			||||||
 | 
					                "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
 | 
				
			||||||
 | 
					                "shasum": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require": {
 | 
				
			||||||
 | 
					                "php": "^7.2 || ^8.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require-dev": {
 | 
				
			||||||
 | 
					                "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
 | 
				
			||||||
 | 
					                "phpcompatibility/php-compatibility": "^9.3",
 | 
				
			||||||
 | 
					                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
 | 
				
			||||||
 | 
					                "squizlabs/php_codesniffer": "^3.7"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "type": "library",
 | 
				
			||||||
 | 
					            "autoload": {
 | 
				
			||||||
 | 
					                "psr-4": {
 | 
				
			||||||
 | 
					                    "Complex\\": "classes/src/"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "notification-url": "https://packagist.org/downloads/",
 | 
				
			||||||
 | 
					            "license": [
 | 
				
			||||||
 | 
					                "MIT"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "authors": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Mark Baker",
 | 
				
			||||||
 | 
					                    "email": "mark@lange.demon.co.uk"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "description": "PHP Class for working with complex numbers",
 | 
				
			||||||
 | 
					            "homepage": "https://github.com/MarkBaker/PHPComplex",
 | 
				
			||||||
 | 
					            "keywords": [
 | 
				
			||||||
 | 
					                "complex",
 | 
				
			||||||
 | 
					                "mathematics"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "support": {
 | 
				
			||||||
 | 
					                "issues": "https://github.com/MarkBaker/PHPComplex/issues",
 | 
				
			||||||
 | 
					                "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "time": "2022-12-06T16:21:08+00:00"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "markbaker/matrix",
 | 
				
			||||||
 | 
					            "version": "3.0.1",
 | 
				
			||||||
 | 
					            "source": {
 | 
				
			||||||
 | 
					                "type": "git",
 | 
				
			||||||
 | 
					                "url": "https://github.com/MarkBaker/PHPMatrix.git",
 | 
				
			||||||
 | 
					                "reference": "728434227fe21be27ff6d86621a1b13107a2562c"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "dist": {
 | 
				
			||||||
 | 
					                "type": "zip",
 | 
				
			||||||
 | 
					                "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c",
 | 
				
			||||||
 | 
					                "reference": "728434227fe21be27ff6d86621a1b13107a2562c",
 | 
				
			||||||
 | 
					                "shasum": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require": {
 | 
				
			||||||
 | 
					                "php": "^7.1 || ^8.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require-dev": {
 | 
				
			||||||
 | 
					                "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
 | 
				
			||||||
 | 
					                "phpcompatibility/php-compatibility": "^9.3",
 | 
				
			||||||
 | 
					                "phpdocumentor/phpdocumentor": "2.*",
 | 
				
			||||||
 | 
					                "phploc/phploc": "^4.0",
 | 
				
			||||||
 | 
					                "phpmd/phpmd": "2.*",
 | 
				
			||||||
 | 
					                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
 | 
				
			||||||
 | 
					                "sebastian/phpcpd": "^4.0",
 | 
				
			||||||
 | 
					                "squizlabs/php_codesniffer": "^3.7"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "type": "library",
 | 
				
			||||||
 | 
					            "autoload": {
 | 
				
			||||||
 | 
					                "psr-4": {
 | 
				
			||||||
 | 
					                    "Matrix\\": "classes/src/"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "notification-url": "https://packagist.org/downloads/",
 | 
				
			||||||
 | 
					            "license": [
 | 
				
			||||||
 | 
					                "MIT"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "authors": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Mark Baker",
 | 
				
			||||||
 | 
					                    "email": "mark@demon-angel.eu"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "description": "PHP Class for working with matrices",
 | 
				
			||||||
 | 
					            "homepage": "https://github.com/MarkBaker/PHPMatrix",
 | 
				
			||||||
 | 
					            "keywords": [
 | 
				
			||||||
 | 
					                "mathematics",
 | 
				
			||||||
 | 
					                "matrix",
 | 
				
			||||||
 | 
					                "vector"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "support": {
 | 
				
			||||||
 | 
					                "issues": "https://github.com/MarkBaker/PHPMatrix/issues",
 | 
				
			||||||
 | 
					                "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "time": "2022-12-02T22:17:43+00:00"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "maximebf/debugbar",
 | 
					            "name": "maximebf/debugbar",
 | 
				
			||||||
            "version": "v1.22.3",
 | 
					            "version": "v1.22.3",
 | 
				
			||||||
| 
						 | 
					@ -2890,6 +3301,111 @@
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "time": "2024-01-28T10:04:15+00:00"
 | 
					            "time": "2024-01-28T10:04:15+00:00"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "phpoffice/phpspreadsheet",
 | 
				
			||||||
 | 
					            "version": "1.29.0",
 | 
				
			||||||
 | 
					            "source": {
 | 
				
			||||||
 | 
					                "type": "git",
 | 
				
			||||||
 | 
					                "url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
 | 
				
			||||||
 | 
					                "reference": "fde2ccf55eaef7e86021ff1acce26479160a0fa0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "dist": {
 | 
				
			||||||
 | 
					                "type": "zip",
 | 
				
			||||||
 | 
					                "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/fde2ccf55eaef7e86021ff1acce26479160a0fa0",
 | 
				
			||||||
 | 
					                "reference": "fde2ccf55eaef7e86021ff1acce26479160a0fa0",
 | 
				
			||||||
 | 
					                "shasum": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require": {
 | 
				
			||||||
 | 
					                "ext-ctype": "*",
 | 
				
			||||||
 | 
					                "ext-dom": "*",
 | 
				
			||||||
 | 
					                "ext-fileinfo": "*",
 | 
				
			||||||
 | 
					                "ext-gd": "*",
 | 
				
			||||||
 | 
					                "ext-iconv": "*",
 | 
				
			||||||
 | 
					                "ext-libxml": "*",
 | 
				
			||||||
 | 
					                "ext-mbstring": "*",
 | 
				
			||||||
 | 
					                "ext-simplexml": "*",
 | 
				
			||||||
 | 
					                "ext-xml": "*",
 | 
				
			||||||
 | 
					                "ext-xmlreader": "*",
 | 
				
			||||||
 | 
					                "ext-xmlwriter": "*",
 | 
				
			||||||
 | 
					                "ext-zip": "*",
 | 
				
			||||||
 | 
					                "ext-zlib": "*",
 | 
				
			||||||
 | 
					                "ezyang/htmlpurifier": "^4.15",
 | 
				
			||||||
 | 
					                "maennchen/zipstream-php": "^2.1 || ^3.0",
 | 
				
			||||||
 | 
					                "markbaker/complex": "^3.0",
 | 
				
			||||||
 | 
					                "markbaker/matrix": "^3.0",
 | 
				
			||||||
 | 
					                "php": "^7.4 || ^8.0",
 | 
				
			||||||
 | 
					                "psr/http-client": "^1.0",
 | 
				
			||||||
 | 
					                "psr/http-factory": "^1.0",
 | 
				
			||||||
 | 
					                "psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require-dev": {
 | 
				
			||||||
 | 
					                "dealerdirect/phpcodesniffer-composer-installer": "dev-main",
 | 
				
			||||||
 | 
					                "dompdf/dompdf": "^1.0 || ^2.0",
 | 
				
			||||||
 | 
					                "friendsofphp/php-cs-fixer": "^3.2",
 | 
				
			||||||
 | 
					                "mitoteam/jpgraph": "^10.3",
 | 
				
			||||||
 | 
					                "mpdf/mpdf": "^8.1.1",
 | 
				
			||||||
 | 
					                "phpcompatibility/php-compatibility": "^9.3",
 | 
				
			||||||
 | 
					                "phpstan/phpstan": "^1.1",
 | 
				
			||||||
 | 
					                "phpstan/phpstan-phpunit": "^1.0",
 | 
				
			||||||
 | 
					                "phpunit/phpunit": "^8.5 || ^9.0 || ^10.0",
 | 
				
			||||||
 | 
					                "squizlabs/php_codesniffer": "^3.7",
 | 
				
			||||||
 | 
					                "tecnickcom/tcpdf": "^6.5"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "suggest": {
 | 
				
			||||||
 | 
					                "dompdf/dompdf": "Option for rendering PDF with PDF Writer",
 | 
				
			||||||
 | 
					                "ext-intl": "PHP Internationalization Functions",
 | 
				
			||||||
 | 
					                "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
 | 
				
			||||||
 | 
					                "mpdf/mpdf": "Option for rendering PDF with PDF Writer",
 | 
				
			||||||
 | 
					                "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "type": "library",
 | 
				
			||||||
 | 
					            "autoload": {
 | 
				
			||||||
 | 
					                "psr-4": {
 | 
				
			||||||
 | 
					                    "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "notification-url": "https://packagist.org/downloads/",
 | 
				
			||||||
 | 
					            "license": [
 | 
				
			||||||
 | 
					                "MIT"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "authors": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Maarten Balliauw",
 | 
				
			||||||
 | 
					                    "homepage": "https://blog.maartenballiauw.be"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Mark Baker",
 | 
				
			||||||
 | 
					                    "homepage": "https://markbakeruk.net"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Franck Lefevre",
 | 
				
			||||||
 | 
					                    "homepage": "https://rootslabs.net"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Erik Tilt"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Adrien Crivelli"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
 | 
				
			||||||
 | 
					            "homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
 | 
				
			||||||
 | 
					            "keywords": [
 | 
				
			||||||
 | 
					                "OpenXML",
 | 
				
			||||||
 | 
					                "excel",
 | 
				
			||||||
 | 
					                "gnumeric",
 | 
				
			||||||
 | 
					                "ods",
 | 
				
			||||||
 | 
					                "php",
 | 
				
			||||||
 | 
					                "spreadsheet",
 | 
				
			||||||
 | 
					                "xls",
 | 
				
			||||||
 | 
					                "xlsx"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "support": {
 | 
				
			||||||
 | 
					                "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
 | 
				
			||||||
 | 
					                "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.29.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "time": "2023-06-14T22:48:31+00:00"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "phpoption/phpoption",
 | 
					            "name": "phpoption/phpoption",
 | 
				
			||||||
            "version": "1.9.2",
 | 
					            "version": "1.9.2",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -183,8 +183,8 @@
 | 
				
			||||||
        <span class="four"><span class="screen-reader-text">4</span></span>
 | 
					        <span class="four"><span class="screen-reader-text">4</span></span>
 | 
				
			||||||
    </section>
 | 
					    </section>
 | 
				
			||||||
    <div class="link-container">
 | 
					    <div class="link-container">
 | 
				
			||||||
        <a href="{{route('home')}}"
 | 
					        <!-- <a href="{{route('home')}}"
 | 
				
			||||||
            class="more-link">Back to home page</a>
 | 
					            class="more-link">Back to home page</a> -->
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,4 +53,11 @@ export const statisticRevenuesByMonth = API_URL + 'v1/admin/dashboard/statistics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Tracking
 | 
					// Tracking
 | 
				
			||||||
export const getListTracking = API_URL + 'v1/admin/tracking'
 | 
					export const getListTracking = API_URL + 'v1/admin/tracking'
 | 
				
			||||||
export const deleteTracking = API_URL + 'v1/admin/tracking/delete'
 | 
					export const deleteTracking = API_URL + 'v1/admin/tracking/delete'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// // Tracking
 | 
				
			||||||
 | 
					export const fetchAllIssues = API_URL + 'v1/admin/jira/fetch-issues'
 | 
				
			||||||
 | 
					export const exportIssues = API_URL + 'v1/admin/jira/export-issues'
 | 
				
			||||||
 | 
					export const getAllProjects = API_URL + 'v1/admin/jira/all-project'
 | 
				
			||||||
 | 
					export const getAllIssuesByProject = API_URL + 'v1/admin/jira/all-issue-by-project'
 | 
				
			||||||
 | 
					export const getAllUserWorklogs = API_URL + 'v1/admin/jira/worklogs'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,8 +25,10 @@ import {
 | 
				
			||||||
  // IconMail,
 | 
					  // IconMail,
 | 
				
			||||||
  IconMoon,
 | 
					  IconMoon,
 | 
				
			||||||
  IconPasswordUser,
 | 
					  IconPasswordUser,
 | 
				
			||||||
 | 
					  IconReport,
 | 
				
			||||||
  IconScan,
 | 
					  IconScan,
 | 
				
			||||||
  IconSettings,
 | 
					  IconSettings,
 | 
				
			||||||
 | 
					  IconSubtask,
 | 
				
			||||||
  IconSun
 | 
					  IconSun
 | 
				
			||||||
} from '@tabler/icons-react'
 | 
					} from '@tabler/icons-react'
 | 
				
			||||||
import { useCallback, useEffect, useState } from 'react'
 | 
					import { useCallback, useEffect, useState } from 'react'
 | 
				
			||||||
| 
						 | 
					@ -37,6 +39,8 @@ import classes from './NavbarSimpleColored.module.css'
 | 
				
			||||||
const data = [
 | 
					const data = [
 | 
				
			||||||
  // { link: '/dashboard', label: 'Dashboard', icon: IconHome },
 | 
					  // { link: '/dashboard', label: 'Dashboard', icon: IconHome },
 | 
				
			||||||
  { link: '/tracking', label: 'Check in/out', icon: IconScan },
 | 
					  { link: '/tracking', label: 'Check in/out', icon: IconScan },
 | 
				
			||||||
 | 
					  { link: '/worklogs', label: 'Worklogs', icon: IconReport },
 | 
				
			||||||
 | 
					  { link: '/jira', label: 'Jira', icon: IconSubtask },
 | 
				
			||||||
  { link: '/custom-theme', label: 'Custom Theme', icon: IconBrush },
 | 
					  { link: '/custom-theme', label: 'Custom Theme', icon: IconBrush },
 | 
				
			||||||
  { link: '/general-setting', label: 'General Setting', icon: IconSettings },
 | 
					  { link: '/general-setting', label: 'General Setting', icon: IconSettings },
 | 
				
			||||||
  // { link: '/packages', label: 'Packages', icon: IconPackages },
 | 
					  // { link: '/packages', label: 'Packages', icon: IconPackages },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					.root {
 | 
				
			||||||
 | 
					    --link-height: rem(38px);
 | 
				
			||||||
 | 
					    --indicator-size: rem(10px);
 | 
				
			||||||
 | 
					    --indicator-offset: calc((var(--link-height) - var(--indicator-size)) / 2);
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    /* padding-left: 20px; */
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .link {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    text-decoration: none;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    color: var(--mantine-color-text);
 | 
				
			||||||
 | 
					    line-height: var(--link-height);
 | 
				
			||||||
 | 
					    font-size: var(--mantine-font-size-sm);
 | 
				
			||||||
 | 
					    height: var(--link-height);
 | 
				
			||||||
 | 
					    border-top-right-radius: var(--mantine-radius-sm);
 | 
				
			||||||
 | 
					    border-bottom-right-radius: var(--mantine-radius-sm);
 | 
				
			||||||
 | 
					    border-left: rem(2px) solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-4));
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    @mixin hover {
 | 
				
			||||||
 | 
					      background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .linkActive {
 | 
				
			||||||
 | 
					    font-weight: 500;
 | 
				
			||||||
 | 
					    color: light-dark(var(--mantine-color-blue-7), var(--mantine-color-blue-4));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .indicator {
 | 
				
			||||||
 | 
					    transition: transform 150ms ease;
 | 
				
			||||||
 | 
					    border: rem(2px) solid light-dark(var(--mantine-color-blue-7), var(--mantine-color-blue-4));
 | 
				
			||||||
 | 
					    background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7));
 | 
				
			||||||
 | 
					    height: var(--indicator-size);
 | 
				
			||||||
 | 
					    width: var(--indicator-size);
 | 
				
			||||||
 | 
					    border-radius: var(--indicator-size);
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    left: calc(var(--indicator-size) / -2 + rem(1));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,47 @@
 | 
				
			||||||
 | 
					import { Avatar, Box } from '@mantine/core';
 | 
				
			||||||
 | 
					import cx from 'clsx';
 | 
				
			||||||
 | 
					import { useState } from 'react';
 | 
				
			||||||
 | 
					import classes from './TableOfContentsFloating.module.css';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TLink = {
 | 
				
			||||||
 | 
					    label: string
 | 
				
			||||||
 | 
					    link: string
 | 
				
			||||||
 | 
					    order: number
 | 
				
			||||||
 | 
					    avartar: string
 | 
				
			||||||
 | 
					    key: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export function TableOfContentsFloating({links, defaultActive, setCurrentProject, }
 | 
				
			||||||
 | 
					  :{links:TLink[],defaultActive:number, setCurrentProject:(name:string)=>void}) {
 | 
				
			||||||
 | 
					  const [active, setActive] = useState(defaultActive);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const items = links.map((item, index) => (
 | 
				
			||||||
 | 
					    <Box<'a'>
 | 
				
			||||||
 | 
					      component="a"
 | 
				
			||||||
 | 
					      href={item.link}
 | 
				
			||||||
 | 
					      onClick={(event) => {
 | 
				
			||||||
 | 
					        event.preventDefault();
 | 
				
			||||||
 | 
					        setCurrentProject(item.label)
 | 
				
			||||||
 | 
					        setActive(index);
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      key={item.label}
 | 
				
			||||||
 | 
					      className={cx(classes.link, { [classes.linkActive]: active === index })}
 | 
				
			||||||
 | 
					      style={{ paddingLeft: `calc(${item.order} * var(--mantine-spacing-md))`}}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <Avatar src={item.avartar} size={'xs'} mr={5}/>{item.label}
 | 
				
			||||||
 | 
					    </Box>
 | 
				
			||||||
 | 
					  ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className={classes.root}>
 | 
				
			||||||
 | 
					      <div className={classes.links}>
 | 
				
			||||||
 | 
					        <div
 | 
				
			||||||
 | 
					          className={classes.indicator}
 | 
				
			||||||
 | 
					          style={{
 | 
				
			||||||
 | 
					            transform: `translateY(calc(${active} * var(--link-height) + var(--indicator-offset)))`
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        {items}
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					.title {
 | 
				
			||||||
 | 
					    background-color: light-dark(var(white), var(--mantine-color-dark-7));
 | 
				
			||||||
 | 
					    z-index: 100;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    justify-content: space-between;
 | 
				
			||||||
 | 
					    padding: 0 var(--mantine-spacing-sm) var(--mantine-spacing-lg)
 | 
				
			||||||
 | 
					      var(--mantine-spacing-sm);
 | 
				
			||||||
 | 
					    border-bottom: solid rgba(201, 201, 201, 0.377) 1px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,155 @@
 | 
				
			||||||
 | 
					import { getAllIssuesByProject, getAllProjects } from '@/api/Admin'
 | 
				
			||||||
 | 
					import { TableOfContentsFloating } from '@/components/TableOfContentsFloating/TableOfContentsFloating'
 | 
				
			||||||
 | 
					import { get } from '@/rtk/helpers/apiService'
 | 
				
			||||||
 | 
					import { Box, Loader, Text } from '@mantine/core'
 | 
				
			||||||
 | 
					import { useEffect, useState } from 'react'
 | 
				
			||||||
 | 
					import classes from './Jira.module.css'
 | 
				
			||||||
 | 
					type TProject = {
 | 
				
			||||||
 | 
					  id: string
 | 
				
			||||||
 | 
					  name: string
 | 
				
			||||||
 | 
					  key: string
 | 
				
			||||||
 | 
					  avatarUrls: {
 | 
				
			||||||
 | 
					    '48x48': string
 | 
				
			||||||
 | 
					    '24x24': string
 | 
				
			||||||
 | 
					    '16x16': string
 | 
				
			||||||
 | 
					    '32x32': string
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TIssue = {
 | 
				
			||||||
 | 
					  summary: string
 | 
				
			||||||
 | 
					  desc: string | null
 | 
				
			||||||
 | 
					  assignee: string | null
 | 
				
			||||||
 | 
					  status: string
 | 
				
			||||||
 | 
					  worklogs: any[]
 | 
				
			||||||
 | 
					  originalEstimate: number | null
 | 
				
			||||||
 | 
					  timeSpent: number | null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const Jira = () => {
 | 
				
			||||||
 | 
					  const [loader, setLoader] = useState(true)
 | 
				
			||||||
 | 
					  const [data, setData] = useState([])
 | 
				
			||||||
 | 
					  const [issuesInProject, setIssuesInProject] = useState<TIssue[]>([])
 | 
				
			||||||
 | 
					  const [listProject, setListProject] = useState<TProject[]>([])
 | 
				
			||||||
 | 
					  const [listStatus, setListStatus] = useState<string[]>([])
 | 
				
			||||||
 | 
					  const [currentProject, setCurrentProject] = useState<string>('Summary')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const getUniqueStatuses = (issues: TIssue[]) => {
 | 
				
			||||||
 | 
					    const statuses = issues.map((issue) => issue.status)
 | 
				
			||||||
 | 
					    const uniqueStatuses = [...new Set(statuses)]
 | 
				
			||||||
 | 
					    return uniqueStatuses
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const getAllIssuesInProject = async (key: string, name: string) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      if (name !== 'Summary') {
 | 
				
			||||||
 | 
					        const res = await get(getAllIssuesByProject, { key: key, name: name })
 | 
				
			||||||
 | 
					        if (res.status) {
 | 
				
			||||||
 | 
					          var statusArray:string[] = getUniqueStatuses(res.data[0].issues)
 | 
				
			||||||
 | 
					          setListStatus(statusArray)
 | 
				
			||||||
 | 
					          setIssuesInProject(res.data[0].issues)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const getAllProject = async () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await get(getAllProjects)
 | 
				
			||||||
 | 
					      if (res.status) {
 | 
				
			||||||
 | 
					        var list: TProject[] = [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            id: '',
 | 
				
			||||||
 | 
					            name: 'Summary',
 | 
				
			||||||
 | 
					            key: '',
 | 
				
			||||||
 | 
					            avatarUrls: {
 | 
				
			||||||
 | 
					              '48x48': '',
 | 
				
			||||||
 | 
					              '24x24': '',
 | 
				
			||||||
 | 
					              '16x16': '',
 | 
				
			||||||
 | 
					              '32x32': '',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        setListProject(list.concat(res.data))
 | 
				
			||||||
 | 
					        setLoader(false)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  console.log(listStatus)
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    getAllProject()
 | 
				
			||||||
 | 
					  }, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    getAllIssuesInProject(
 | 
				
			||||||
 | 
					      listProject.find((p) => p.name === currentProject)?.key!,
 | 
				
			||||||
 | 
					      currentProject,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }, [currentProject])
 | 
				
			||||||
 | 
					  return loader ? (
 | 
				
			||||||
 | 
					    <Box ta={'center'}>
 | 
				
			||||||
 | 
					      <Loader size={40} mt={'15%'} />
 | 
				
			||||||
 | 
					    </Box>
 | 
				
			||||||
 | 
					  ) : (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <div className={classes.title}>
 | 
				
			||||||
 | 
					        <h3>
 | 
				
			||||||
 | 
					          <Text>Admin/</Text>Jira
 | 
				
			||||||
 | 
					        </h3>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <Box display={'flex'}>
 | 
				
			||||||
 | 
					        <Box w={'20%'} p={'md'}>
 | 
				
			||||||
 | 
					          <Text fw={700} mb={'md'}>
 | 
				
			||||||
 | 
					            Projects
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					          <Box
 | 
				
			||||||
 | 
					            h={'70vh'}
 | 
				
			||||||
 | 
					            style={{
 | 
				
			||||||
 | 
					              display: 'flex',
 | 
				
			||||||
 | 
					              flexFlow: 'column',
 | 
				
			||||||
 | 
					              maxHeight: '70vh',
 | 
				
			||||||
 | 
					              overflow: 'auto',
 | 
				
			||||||
 | 
					              paddingLeft: '20px',
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <TableOfContentsFloating
 | 
				
			||||||
 | 
					              links={listProject.map((p) => {
 | 
				
			||||||
 | 
					                return {
 | 
				
			||||||
 | 
					                  label: p.name,
 | 
				
			||||||
 | 
					                  link: '#',
 | 
				
			||||||
 | 
					                  order: 1,
 | 
				
			||||||
 | 
					                  avartar: p.avatarUrls['16x16'],
 | 
				
			||||||
 | 
					                  key: p.key,
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              })}
 | 
				
			||||||
 | 
					              setCurrentProject={setCurrentProject}
 | 
				
			||||||
 | 
					              defaultActive={0}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
 | 
					        <Box w={'80%'} h={'100vh'} display={"flex"} style={{overflowX:"auto"}}>
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            listStatus?.map((sta)=>(
 | 
				
			||||||
 | 
					                <Box p={'sm'} style={{border:"solid 1px gray"}}>
 | 
				
			||||||
 | 
					                    <Text ta={'center'} style={{border:"solid 1px gray", borderRadius:"8px"}} mb={"md"} fw={600}>{sta}</Text>
 | 
				
			||||||
 | 
					                    <Box style={{maxHeight:"90vh", overflowX:"hidden"}}>
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                             issuesInProject.filter((iss)=>iss.status === sta)?.map((iss) => (
 | 
				
			||||||
 | 
					                                <Text w={"200px"}  fz={15} style={{border:"solid 1px gray", padding:"4px", marginBottom:"5px"}}>{iss.summary}</Text>
 | 
				
			||||||
 | 
					                              ))
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    </Box>
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					            ))
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
 | 
					      </Box>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Jira
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					.title {
 | 
				
			||||||
 | 
					    background-color: light-dark(var(white), var(--mantine-color-dark-7));
 | 
				
			||||||
 | 
					    z-index: 100;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    justify-content: space-between;
 | 
				
			||||||
 | 
					    padding: 0 var(--mantine-spacing-sm) var(--mantine-spacing-lg)
 | 
				
			||||||
 | 
					      var(--mantine-spacing-sm);
 | 
				
			||||||
 | 
					    border-bottom: solid rgba(201, 201, 201, 0.377) 1px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,373 @@
 | 
				
			||||||
 | 
					import { Avatar, Box, Button, Loader, Text } from '@mantine/core'
 | 
				
			||||||
 | 
					import React, { useEffect, useState } from 'react'
 | 
				
			||||||
 | 
					import classes from './Worklogs.module.css'
 | 
				
			||||||
 | 
					import { get } from '@/rtk/helpers/apiService'
 | 
				
			||||||
 | 
					import { getAllUserWorklogs } from '@/api/Admin'
 | 
				
			||||||
 | 
					import moment from 'moment'
 | 
				
			||||||
 | 
					import { DateInput, DatePicker } from '@mantine/dates'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface WorkLog {
 | 
				
			||||||
 | 
					  self: string
 | 
				
			||||||
 | 
					  author: UserInfo
 | 
				
			||||||
 | 
					  updateAuthor: UserInfo
 | 
				
			||||||
 | 
					  created: string
 | 
				
			||||||
 | 
					  updated: string
 | 
				
			||||||
 | 
					  started: string
 | 
				
			||||||
 | 
					  timeSpent: string
 | 
				
			||||||
 | 
					  timeSpentSeconds: number
 | 
				
			||||||
 | 
					  id: string
 | 
				
			||||||
 | 
					  issueId: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface UserInfo {
 | 
				
			||||||
 | 
					  self: string
 | 
				
			||||||
 | 
					  accountId: string
 | 
				
			||||||
 | 
					  avatarUrls: AvatarUrls
 | 
				
			||||||
 | 
					  displayName: string
 | 
				
			||||||
 | 
					  active: boolean
 | 
				
			||||||
 | 
					  timeZone: string
 | 
				
			||||||
 | 
					  accountType: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface AvatarUrls {
 | 
				
			||||||
 | 
					  '48x48': string
 | 
				
			||||||
 | 
					  '24x24': string
 | 
				
			||||||
 | 
					  '16x16': string
 | 
				
			||||||
 | 
					  '32x32': string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface Issue {
 | 
				
			||||||
 | 
					  expand: string
 | 
				
			||||||
 | 
					  id: string
 | 
				
			||||||
 | 
					  self: string
 | 
				
			||||||
 | 
					  key: string
 | 
				
			||||||
 | 
					  fields: IssueFields
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IssueFields {
 | 
				
			||||||
 | 
					  summary: string
 | 
				
			||||||
 | 
					  timespent: number
 | 
				
			||||||
 | 
					  timeoriginalestimate: number
 | 
				
			||||||
 | 
					  project: ProjectInfo
 | 
				
			||||||
 | 
					  worklog: WorkLogList
 | 
				
			||||||
 | 
					  status: StatusInfo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface ProjectInfo {
 | 
				
			||||||
 | 
					  self: string
 | 
				
			||||||
 | 
					  id: string
 | 
				
			||||||
 | 
					  key: string
 | 
				
			||||||
 | 
					  name: string
 | 
				
			||||||
 | 
					  projectTypeKey: string
 | 
				
			||||||
 | 
					  simplified: boolean
 | 
				
			||||||
 | 
					  avatarUrls: AvatarUrls
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface WorkLogList {
 | 
				
			||||||
 | 
					  startAt: number
 | 
				
			||||||
 | 
					  maxResults: number
 | 
				
			||||||
 | 
					  total: number
 | 
				
			||||||
 | 
					  worklogs: WorkLog[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface StatusInfo {
 | 
				
			||||||
 | 
					  self: string
 | 
				
			||||||
 | 
					  description: string
 | 
				
			||||||
 | 
					  iconUrl: string
 | 
				
			||||||
 | 
					  name: string
 | 
				
			||||||
 | 
					  id: string
 | 
				
			||||||
 | 
					  statusCategory: StatusCategory
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface StatusCategory {
 | 
				
			||||||
 | 
					  self: string
 | 
				
			||||||
 | 
					  id: number
 | 
				
			||||||
 | 
					  key: string
 | 
				
			||||||
 | 
					  colorName: string
 | 
				
			||||||
 | 
					  name: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface Information {
 | 
				
			||||||
 | 
					  startAt: number
 | 
				
			||||||
 | 
					  maxResults: number
 | 
				
			||||||
 | 
					  total: number
 | 
				
			||||||
 | 
					  issues: Issue[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface UserWorklog {
 | 
				
			||||||
 | 
					  username: string
 | 
				
			||||||
 | 
					  information: Information
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const Worklogs = () => {
 | 
				
			||||||
 | 
					  const [loader, setLoader] = useState(true)
 | 
				
			||||||
 | 
					  const [updating, setUpdating] = useState(true)
 | 
				
			||||||
 | 
					  const [worklogs, setWorklogs] = useState<UserWorklog[]>([])
 | 
				
			||||||
 | 
					  const [date, setDate] = useState({
 | 
				
			||||||
 | 
					    startDate: moment(Date.now()).format('YYYY-MM-DD'),
 | 
				
			||||||
 | 
					    endDate: moment(Date.now()).format('YYYY-MM-DD'),
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const getAllWorklogs = async (
 | 
				
			||||||
 | 
					    startDate = date.startDate,
 | 
				
			||||||
 | 
					    endDate = date.endDate,
 | 
				
			||||||
 | 
					  ) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      setUpdating(false)
 | 
				
			||||||
 | 
					      const res = await get(getAllUserWorklogs, {
 | 
				
			||||||
 | 
					        startDate: startDate,
 | 
				
			||||||
 | 
					        endDate: endDate,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      if (res.status) {
 | 
				
			||||||
 | 
					        const data = res.data.filter(
 | 
				
			||||||
 | 
					          (user: UserWorklog) => user.information.issues.length > 0,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        setWorklogs(data)
 | 
				
			||||||
 | 
					        localStorage.setItem(
 | 
				
			||||||
 | 
					          'data',
 | 
				
			||||||
 | 
					          JSON.stringify({ time: Date.now(), data: data, date: date }),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        setLoader(false)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      setUpdating(true)
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    const localData = localStorage.getItem('data')
 | 
				
			||||||
 | 
					    if (localData !== null) {
 | 
				
			||||||
 | 
					      setWorklogs(JSON.parse(localData!).data)
 | 
				
			||||||
 | 
					      setDate(JSON.parse(localData!).date)
 | 
				
			||||||
 | 
					      setLoader(false)
 | 
				
			||||||
 | 
					      if (Date.now() - JSON.parse(localData!).time > 300000) {
 | 
				
			||||||
 | 
					        getAllWorklogs()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      getAllWorklogs()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return loader ? (
 | 
				
			||||||
 | 
					    <Box ta={'center'}>
 | 
				
			||||||
 | 
					      <Loader size={40} mt={'15%'} />
 | 
				
			||||||
 | 
					    </Box>
 | 
				
			||||||
 | 
					  ) : (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <div className={classes.title}>
 | 
				
			||||||
 | 
					        <h3>
 | 
				
			||||||
 | 
					          <Text>Admin/</Text>Worklogs
 | 
				
			||||||
 | 
					          {!updating ? (
 | 
				
			||||||
 | 
					            <Text fs={'italic'} fz={'xs'} c={'gray'}>
 | 
				
			||||||
 | 
					              Updating data in the background ...
 | 
				
			||||||
 | 
					            </Text>
 | 
				
			||||||
 | 
					          ) : (
 | 
				
			||||||
 | 
					            ''
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					        </h3>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <Box
 | 
				
			||||||
 | 
					        display={'flex'}
 | 
				
			||||||
 | 
					        w={'30%'}
 | 
				
			||||||
 | 
					        style={{
 | 
				
			||||||
 | 
					          float: 'right',
 | 
				
			||||||
 | 
					          margin: '10px',
 | 
				
			||||||
 | 
					          alignItems: 'end',
 | 
				
			||||||
 | 
					          justifyContent: 'space-between',
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <DateInput
 | 
				
			||||||
 | 
					          size="xs"
 | 
				
			||||||
 | 
					          label="From date:"
 | 
				
			||||||
 | 
					          value={new Date(date.startDate)}
 | 
				
			||||||
 | 
					          w={'40%'}
 | 
				
			||||||
 | 
					          clearable
 | 
				
			||||||
 | 
					          onChange={(e) => {
 | 
				
			||||||
 | 
					            setDate({ ...date, startDate: moment(e).format('YYYY-MM-DD') })
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <DateInput
 | 
				
			||||||
 | 
					          size="xs"
 | 
				
			||||||
 | 
					          label="To date:"
 | 
				
			||||||
 | 
					          value={new Date(date.endDate)}
 | 
				
			||||||
 | 
					          clearable
 | 
				
			||||||
 | 
					          w={'40%'}
 | 
				
			||||||
 | 
					          onChange={(e) => {
 | 
				
			||||||
 | 
					            setDate({ ...date, endDate: moment(e).format('YYYY-MM-DD') })
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <Button
 | 
				
			||||||
 | 
					          size="xs"
 | 
				
			||||||
 | 
					          onClick={() => {
 | 
				
			||||||
 | 
					            getAllWorklogs()
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          Search
 | 
				
			||||||
 | 
					        </Button>
 | 
				
			||||||
 | 
					      </Box>
 | 
				
			||||||
 | 
					      <Box
 | 
				
			||||||
 | 
					        w={'100%'}
 | 
				
			||||||
 | 
					        h={'85vh'}
 | 
				
			||||||
 | 
					        display={'flex'}
 | 
				
			||||||
 | 
					        style={{ overflowX: 'auto', flexFlow: 'column' }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        {worklogs?.map((user, index) => (
 | 
				
			||||||
 | 
					          <Box
 | 
				
			||||||
 | 
					            p={'sm'}
 | 
				
			||||||
 | 
					            style={{
 | 
				
			||||||
 | 
					              border: 'solid 1px gray',
 | 
				
			||||||
 | 
					              borderColor: '#afafaf',
 | 
				
			||||||
 | 
					              marginBottom: '10px',
 | 
				
			||||||
 | 
					              backgroundColor:
 | 
				
			||||||
 | 
					                index % 2 === 0 ? 'rgb(201 201 201 / 28%)' : 'white',
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <Text ta={'left'} mb={'xs'} fw={800}>
 | 
				
			||||||
 | 
					              {user.username} 
 | 
				
			||||||
 | 
					              ({user?.information.issues.reduce(
 | 
				
			||||||
 | 
					                (total: number, issue: Issue) => {
 | 
				
			||||||
 | 
					                  var totalSpent = issue.fields.worklog.worklogs?.reduce(
 | 
				
			||||||
 | 
					                    (accumulator: number, currentValue: WorkLog) => {
 | 
				
			||||||
 | 
					                      if (
 | 
				
			||||||
 | 
					                        parseInt(moment(date.startDate).format('YYYYMMDD')) <=
 | 
				
			||||||
 | 
					                          parseInt(
 | 
				
			||||||
 | 
					                            moment(currentValue.started).format('YYYYMMDD'),
 | 
				
			||||||
 | 
					                          ) &&
 | 
				
			||||||
 | 
					                        parseInt(
 | 
				
			||||||
 | 
					                          moment(currentValue.started).format('YYYYMMDD'),
 | 
				
			||||||
 | 
					                        ) <= parseInt(moment(date.endDate).format('YYYYMMDD')) && currentValue.updateAuthor.displayName === user.username
 | 
				
			||||||
 | 
					                      ) {
 | 
				
			||||||
 | 
					                        return accumulator + currentValue.timeSpentSeconds
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                      return accumulator
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    0,
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
 | 
					                  return total + totalSpent
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                0,
 | 
				
			||||||
 | 
					              ) /
 | 
				
			||||||
 | 
					                60 /
 | 
				
			||||||
 | 
					                60}
 | 
				
			||||||
 | 
					              h)
 | 
				
			||||||
 | 
					            </Text>
 | 
				
			||||||
 | 
					            {user.information.issues.map((iss) => {
 | 
				
			||||||
 | 
					              if (
 | 
				
			||||||
 | 
					                iss.fields.worklog.worklogs.filter(
 | 
				
			||||||
 | 
					                  (w) =>
 | 
				
			||||||
 | 
					                    parseInt(moment(date.startDate).format('YYYYMMDD')) <=
 | 
				
			||||||
 | 
					                      parseInt(moment(w.started).format('YYYYMMDD')) &&
 | 
				
			||||||
 | 
					                    parseInt(moment(date.endDate).format('YYYYMMDD')) >=
 | 
				
			||||||
 | 
					                      parseInt(moment(w.started).format('YYYYMMDD')) && w.updateAuthor.displayName === user.username
 | 
				
			||||||
 | 
					                ).length > 0
 | 
				
			||||||
 | 
					              ) {
 | 
				
			||||||
 | 
					                return (
 | 
				
			||||||
 | 
					                  <Box
 | 
				
			||||||
 | 
					                    style={{
 | 
				
			||||||
 | 
					                      border: 'solid 1px gray',
 | 
				
			||||||
 | 
					                      padding: '10px',
 | 
				
			||||||
 | 
					                      marginBottom: '5px',
 | 
				
			||||||
 | 
					                      maxHeight: '90vh',
 | 
				
			||||||
 | 
					                      overflowX: 'hidden',
 | 
				
			||||||
 | 
					                      color: '#412d2d',
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    <Box>
 | 
				
			||||||
 | 
					                      <Text
 | 
				
			||||||
 | 
					                        fz={14}
 | 
				
			||||||
 | 
					                        display={'flex'}
 | 
				
			||||||
 | 
					                        style={{ alignItems: 'center' }}
 | 
				
			||||||
 | 
					                      >
 | 
				
			||||||
 | 
					                        <b>Summary:</b>{' '}
 | 
				
			||||||
 | 
					                        <Avatar
 | 
				
			||||||
 | 
					                          src={iss.fields.project.avatarUrls['16x16']}
 | 
				
			||||||
 | 
					                          size={'xs'}
 | 
				
			||||||
 | 
					                          m={'0 5px'}
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                        {iss.fields.project.name} -{' '}
 | 
				
			||||||
 | 
					                        <a
 | 
				
			||||||
 | 
					                          href={`https://apactechvn.atlassian.net/browse/${iss.key}`}
 | 
				
			||||||
 | 
					                          target="_blank"
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
 | 
					                          {iss.fields.summary}
 | 
				
			||||||
 | 
					                        </a>
 | 
				
			||||||
 | 
					                      </Text>
 | 
				
			||||||
 | 
					                      <Text fz={14}>
 | 
				
			||||||
 | 
					                        <b>Estimate:</b>{' '}
 | 
				
			||||||
 | 
					                        {iss.fields.timeoriginalestimate / 60 / 60}h
 | 
				
			||||||
 | 
					                      </Text>
 | 
				
			||||||
 | 
					                      <Text fz={14}>
 | 
				
			||||||
 | 
					                        <b>Total time spent:</b>{' '}
 | 
				
			||||||
 | 
					                        {iss.fields.timespent / 60 / 60}h
 | 
				
			||||||
 | 
					                      </Text>
 | 
				
			||||||
 | 
					                      <Text fz={14}>
 | 
				
			||||||
 | 
					                        <b>Time spent/day:</b>{' '}
 | 
				
			||||||
 | 
					                        {iss.fields.worklog.worklogs?.reduce(
 | 
				
			||||||
 | 
					                          (accumulator: number, currentValue: WorkLog) => {
 | 
				
			||||||
 | 
					                            if (
 | 
				
			||||||
 | 
					                              (parseInt(
 | 
				
			||||||
 | 
					                                moment(date.startDate).format('YYYYMMDD'),
 | 
				
			||||||
 | 
					                              ) <=
 | 
				
			||||||
 | 
					                                parseInt(
 | 
				
			||||||
 | 
					                                  moment(currentValue.started).format(
 | 
				
			||||||
 | 
					                                    'YYYYMMDD',
 | 
				
			||||||
 | 
					                                  ),
 | 
				
			||||||
 | 
					                                )) &&
 | 
				
			||||||
 | 
					                              (parseInt(
 | 
				
			||||||
 | 
					                                moment(currentValue.started).format('YYYYMMDD'),
 | 
				
			||||||
 | 
					                              ) <=
 | 
				
			||||||
 | 
					                                parseInt(
 | 
				
			||||||
 | 
					                                  moment(date.endDate).format('YYYYMMDD')
 | 
				
			||||||
 | 
					                                )) && currentValue.updateAuthor.displayName === user.username
 | 
				
			||||||
 | 
					                            ) {
 | 
				
			||||||
 | 
					                              return accumulator + currentValue.timeSpentSeconds
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            return accumulator
 | 
				
			||||||
 | 
					                          },
 | 
				
			||||||
 | 
					                          0,
 | 
				
			||||||
 | 
					                        ) /
 | 
				
			||||||
 | 
					                          60 /
 | 
				
			||||||
 | 
					                          60}
 | 
				
			||||||
 | 
					                        h
 | 
				
			||||||
 | 
					                      </Text>
 | 
				
			||||||
 | 
					                    </Box>
 | 
				
			||||||
 | 
					                    {iss.fields.worklog.worklogs?.map((log) => {
 | 
				
			||||||
 | 
					                      if (
 | 
				
			||||||
 | 
					                        moment(date.startDate).format('YYYYMMDD') <=
 | 
				
			||||||
 | 
					                          moment(log.started).format('YYYYMMDD') &&
 | 
				
			||||||
 | 
					                        moment(log.started).format('YYYYMMDD') <=
 | 
				
			||||||
 | 
					                          moment(date.endDate).format('YYYYMMDD') && log.updateAuthor.displayName === user.username
 | 
				
			||||||
 | 
					                      ) {
 | 
				
			||||||
 | 
					                        return (
 | 
				
			||||||
 | 
					                          <Box
 | 
				
			||||||
 | 
					                            style={{
 | 
				
			||||||
 | 
					                              padding: '4px 8px',
 | 
				
			||||||
 | 
					                              marginBottom: '5px',
 | 
				
			||||||
 | 
					                              marginLeft: '10px',
 | 
				
			||||||
 | 
					                              backgroundColor: '#d9d9d9',
 | 
				
			||||||
 | 
					                            }}
 | 
				
			||||||
 | 
					                          >
 | 
				
			||||||
 | 
					                            <Text fz={13}>
 | 
				
			||||||
 | 
					                              <b>Start date:</b>{' '}
 | 
				
			||||||
 | 
					                              {moment(log.started).format('HH:mm YYYY/MM/DD')}
 | 
				
			||||||
 | 
					                            </Text>
 | 
				
			||||||
 | 
					                            <Text fz={13}>
 | 
				
			||||||
 | 
					                              <b>Time spent:</b> {log.timeSpent}
 | 
				
			||||||
 | 
					                            </Text>
 | 
				
			||||||
 | 
					                          </Box>
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                    })}
 | 
				
			||||||
 | 
					                  </Box>
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            })}
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        ))}
 | 
				
			||||||
 | 
					      </Box>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Worklogs
 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,11 @@ import PageLogin from '@/pages/Auth/Login/Login'
 | 
				
			||||||
import CustomTheme from '@/pages/CustomTheme/CustomTheme'
 | 
					import CustomTheme from '@/pages/CustomTheme/CustomTheme'
 | 
				
			||||||
import Dashboard from '@/pages/Dashboard/Dashboard'
 | 
					import Dashboard from '@/pages/Dashboard/Dashboard'
 | 
				
			||||||
import GeneralSetting from '@/pages/GeneralSetting/GeneralSetting'
 | 
					import GeneralSetting from '@/pages/GeneralSetting/GeneralSetting'
 | 
				
			||||||
 | 
					import Jira from '@/pages/Jira/Jira'
 | 
				
			||||||
import PageNotFound from '@/pages/NotFound/NotFound'
 | 
					import PageNotFound from '@/pages/NotFound/NotFound'
 | 
				
			||||||
import Tracking from '@/pages/Tracking/Tracking'
 | 
					import Tracking from '@/pages/Tracking/Tracking'
 | 
				
			||||||
import PageWelcome from '@/pages/Welcome/Welcome'
 | 
					import PageWelcome from '@/pages/Welcome/Welcome'
 | 
				
			||||||
 | 
					import Worklogs from '@/pages/Worklogs/Worklogs'
 | 
				
			||||||
import { Navigate } from 'react-router-dom'
 | 
					import { Navigate } from 'react-router-dom'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mainRoutes = [
 | 
					const mainRoutes = [
 | 
				
			||||||
| 
						 | 
					@ -85,6 +87,34 @@ const mainRoutes = [
 | 
				
			||||||
      </ProtectedRoute>
 | 
					      </ProtectedRoute>
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    path: '/jira',
 | 
				
			||||||
 | 
					    element: (
 | 
				
			||||||
 | 
					      <ProtectedRoute mode="route">
 | 
				
			||||||
 | 
					        <BasePage
 | 
				
			||||||
 | 
					          main={
 | 
				
			||||||
 | 
					            <>
 | 
				
			||||||
 | 
					              <Jira />
 | 
				
			||||||
 | 
					            </>
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ></BasePage>
 | 
				
			||||||
 | 
					      </ProtectedRoute>
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    path: '/worklogs',
 | 
				
			||||||
 | 
					    element: (
 | 
				
			||||||
 | 
					      <ProtectedRoute mode="route">
 | 
				
			||||||
 | 
					        <BasePage
 | 
				
			||||||
 | 
					          main={
 | 
				
			||||||
 | 
					            <>
 | 
				
			||||||
 | 
					              <Worklogs />
 | 
				
			||||||
 | 
					            </>
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ></BasePage>
 | 
				
			||||||
 | 
					      </ProtectedRoute>
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  // {
 | 
					  // {
 | 
				
			||||||
  //   path: '/packages',
 | 
					  //   path: '/packages',
 | 
				
			||||||
  //   element: (
 | 
					  //   element: (
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue