Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various features and bug fixes #31

Merged
merged 81 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from 80 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
4dccd2b
Created new handler to handle post cases
jeppekroghitk Nov 19, 2024
06d2604
Revised date navigation logic to be more flexible and revised post ha…
jeppekroghitk Nov 19, 2024
eaee2e0
Added new translations
jeppekroghitk Nov 19, 2024
7006864
Added ticket type to select
jeppekroghitk Nov 19, 2024
9910ab8
Refactored table rendition and cleaned up
jeppekroghitk Nov 19, 2024
766ab18
Fixed bugs, added new date range selector, optimizations etc
jeppekroghitk Nov 19, 2024
068b634
Built dist files
jeppekroghitk Nov 19, 2024
e859f7f
Added flatpickr
jeppekroghitk Nov 19, 2024
21e2da7
Updated readme
jeppekroghitk Nov 25, 2024
5187e13
Added compatability for leantime 3.3.x
jeppekroghitk Nov 25, 2024
d472957
Built assets
jeppekroghitk Dec 12, 2024
71e7a12
Updated default ticket cache expiration value to 60
jeppekroghitk Dec 12, 2024
0d798d4
Removed unused userId assignment to template in TimeTable controller
jeppekroghitk Dec 12, 2024
c0a8643
Refactored timetable layout: removed userId attribute and added scrol…
jeppekroghitk Dec 12, 2024
ae0f710
Added styles for timetable scroll container
jeppekroghitk Dec 12, 2024
c4eb522
Integrated pluginSettings for userId and ticketCacheTimeout in TimeTable
jeppekroghitk Dec 12, 2024
579f334
Refactored ticketCacheExpiration to use pluginSettings
jeppekroghitk Dec 12, 2024
530f300
Added timetableSettings script with userId and ticketCacheExpiration …
jeppekroghitk Dec 12, 2024
93fd000
Built assets
jeppekroghitk Dec 12, 2024
72e46a6
Refactored 'get' function to enhance readability, fixed date locale h…
jeppekroghitk Dec 12, 2024
af5d756
Optimized timetable template structure and cleaned up excessive table…
jeppekroghitk Dec 12, 2024
f736ece
Improved sticky table column styles, added borders using pseudo-eleme…
jeppekroghitk Dec 12, 2024
92559bd
Removed unused variables and streamlined initialization logic in Time…
jeppekroghitk Dec 12, 2024
64af248
built assets
jeppekroghitk Dec 12, 2024
109cfb4
Add 'new-week' class logic and improve table structure
jeppekroghitk Dec 12, 2024
88e113c
Add styling for 'new-week' class and sticky headers
jeppekroghitk Dec 12, 2024
8f80260
Implement IntersectionObserver for sticky 'new-week' headers
jeppekroghitk Dec 12, 2024
47378a7
Built assets
jeppekroghitk Dec 13, 2024
5dc925f
Fix: Adjust z-index for timetable cell styling
jeppekroghitk Dec 13, 2024
a736607
Refactor: Remove debug log and fix 'tomselect' method references
jeppekroghitk Dec 13, 2024
c7afdac
Added correct synchronization of projects
jeppekroghitk Dec 13, 2024
023cc1d
Updated changelog
jeppekroghitk Dec 13, 2024
3a1e2e0
Coding standards
jeppekroghitk Dec 13, 2024
b4485d8
Coding standards
jeppekroghitk Dec 13, 2024
62d1669
Coding standards
jeppekroghitk Dec 13, 2024
82a9d06
built assets
jeppekroghitk Dec 14, 2024
2fb1618
Fix typos and minor updates in TimeTable controller.
jeppekroghitk Dec 14, 2024
205ff66
Update method signatures to use CarbonInterface and format workDate.
jeppekroghitk Dec 14, 2024
5286b50
Switch to CarbonInterface in TimeTable service for consistency.
jeppekroghitk Dec 14, 2024
44fee3f
Refactor timetable template to adjust layout and remove redundant ele…
jeppekroghitk Dec 14, 2024
192a974
Update styles for modal and table width adjustments.
jeppekroghitk Dec 14, 2024
19530a6
Clean up JavaScript, improve event handlers, and optimize modal logic.
jeppekroghitk Dec 14, 2024
e160082
built assets
jeppekroghitk Dec 15, 2024
20ad248
Add deleteTicket action handling in TimeTable controller
jeppekroghitk Dec 15, 2024
e86d8f0
Refactor and delegate query parameter handling in TimeTableActionHandler
jeppekroghitk Dec 15, 2024
aa17108
Adjust timetable styles for overflow and spacing changes
jeppekroghitk Dec 15, 2024
e93918f
Implement overflow detection and improve deleteTicket AJAX handling
jeppekroghitk Dec 15, 2024
24bde0c
built assets
jeppekroghitk Dec 16, 2024
6150c5a
Add case handling for copyEntryForward
jeppekroghitk Dec 16, 2024
7fb6df0
Implement copyEntryForward logic and append query params enhancements
jeppekroghitk Dec 16, 2024
eac109f
Add addTimelogOnTicket method for timelog functionality
jeppekroghitk Dec 16, 2024
76bb92e
Expose addTimelogOnTicket in TimeTable service
jeppekroghitk Dec 16, 2024
f525001
Add hidden inputs for week details and copy button element
jeppekroghitk Dec 16, 2024
e13b52f
Add styles for copy button and highlight effects
jeppekroghitk Dec 16, 2024
4400810
Implement entry copy functionality and button interaction for timetable
jeppekroghitk Dec 16, 2024
65eed19
Fixed visibility of week numbers when overflowing
jeppekroghitk Jan 7, 2025
5317ffe
Added translations
jeppekroghitk Jan 8, 2025
8e881a0
Fixed lost get params when posting
jeppekroghitk Jan 8, 2025
f433d77
Passing overwrite param when assigned
jeppekroghitk Jan 8, 2025
4f8e7c5
Overwriting timelogs when passing overwrite param
jeppekroghitk Jan 8, 2025
8eb4381
Implementing timelog copy functionality
jeppekroghitk Jan 8, 2025
b604e8d
built assets
jeppekroghitk Jan 8, 2025
a4864c9
Adjusted some sizing and colorization of entry copy design
jeppekroghitk Jan 9, 2025
1e7811a
Minor styling changes and fixed issue when adding new todo
jeppekroghitk Jan 9, 2025
16ea014
Coding standards
jeppekroghitk Jan 9, 2025
5857015
Coding standards
jeppekroghitk Jan 9, 2025
483fdb3
Coding standards
jeppekroghitk Jan 9, 2025
ebacb8b
Coding standards
jeppekroghitk Jan 9, 2025
4140fa7
Coding standards
jeppekroghitk Jan 9, 2025
8663703
Coding standards
jeppekroghitk Jan 9, 2025
5569dbf
Updated changelog
jeppekroghitk Jan 9, 2025
3c90e7e
Defined default ticket expiration as a class constant
jeppekroghitk Jan 9, 2025
373fc15
Added comment describing functionality of adjustPeriod method
jeppekroghitk Jan 9, 2025
027f2ee
Rewrote post switch to a match statement
jeppekroghitk Jan 9, 2025
b9b7ea3
Rewrote str_contains to use parse_url instead
jeppekroghitk Jan 9, 2025
2f24a2f
Improved english translation
jeppekroghitk Jan 9, 2025
6b8fe74
Updated leantime in composer.json
jeppekroghitk Jan 9, 2025
fea8bf3
Coding standards
jeppekroghitk Jan 9, 2025
09a3361
Coding standards
jeppekroghitk Jan 9, 2025
9c7afb9
Coding standards
jeppekroghitk Jan 9, 2025
cde20b6
Added icon for displaying when a to-do is assigned to the user
jeppekroghitk Jan 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

## [Unreleased]

* [PR-31](https://github.com/ITK-Leantime/leantime-timetable/pull/31)
* Introduced fromDate and toDate, enabling setting (-x day and +x day) as params for relative dynamic urls.
* Introduced FlatPickr for date range selection
* Unlocked range of displayed days at one time
* Overhauled post handling
* Added ticket type to dropdown
* Fixed issue where tickets were not sorted correctly
* Worked on better displaying that the tickets are being synced on load
* Removed "add timelog" button
* Locked save button and show spinner on click
* Implemented overflow-scroll behaviour when selecting many days
* Redesigned modal to popup at clicked cell
* Implemented copy forward functionality
* Added option to overwrite already filled fields when copying forward
* Fixed issue when adding new todo

## [2.0.2] - 2024-10-24

* [PR-29](https://github.com/ITK-Leantime/leantime-timetable/pull/29)
Expand Down
8 changes: 5 additions & 3 deletions Controllers/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Leantime\Plugins\TimeTable\Controllers;

use Leantime\Core\Template;
use Leantime\Core\UI\Template;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller;
use Symfony\Component\HttpFoundation\RedirectResponse;
Expand All @@ -20,6 +20,8 @@ class Settings extends Controller
private SettingRepository $settingsRepo;
protected Template $template;

private const DEFAULT_TICKET_EXPIRATION = 60;

/**
* constructor
* @access public
Expand All @@ -41,7 +43,7 @@ public function init(SettingRepository $settingsRepo, Template $template): void
public function get(): Response
{
try {
$ticketCacheExpiration = (int) ($this->settingsRepo->getSetting('itk-leantime-timetable.ticketCacheExpiration') ?: 120);
$ticketCacheExpiration = (int) ($this->settingsRepo->getSetting('itk-leantime-timetable.ticketCacheExpiration') ?: self::DEFAULT_TICKET_EXPIRATION);
$this->template->assign('ticketCacheExpiration', $ticketCacheExpiration);
} catch (\Exception $e) {
$this->template->setNotification('An error occurred while saving the settings. ' . $e, 'error');
Expand All @@ -60,7 +62,7 @@ public function get(): Response
public function post(array $params): RedirectResponse
{
try {
$this->settingsRepo->saveSetting('itk-leantime-timetable.ticketCacheExpiration', (int)($params['ticketCacheExpiration'] ?? 0));
$this->settingsRepo->saveSetting('itk-leantime-timetable.ticketCacheExpiration', (int)($params['ticketCacheExpiration'] ?? self::DEFAULT_TICKET_EXPIRATION));
$this->template->setNotification('The settings were successfully saved.', 'success');
} catch (\Exception $e) {
$this->template->setNotification('An error occurred while saving the settings. ' . $e, 'error');
Expand Down
134 changes: 76 additions & 58 deletions Controllers/TimeTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
namespace Leantime\Plugins\TimeTable\Controllers;

use Carbon\CarbonImmutable;
use Illuminate\Support\Facades\Log;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller;
use mysql_xdevapi\Exception;
use Leantime\Plugins\TimeTable\Helpers\TimeTableActionHandler;
use Symfony\Component\HttpFoundation\Response;
use Leantime\Plugins\TimeTable\Services\TimeTable as TimeTableService;
use Leantime\Core\Language as LanguageCore;
use Leantime\Domain\Auth\Models\Roles;
use Leantime\Domain\Auth\Services\Auth as AuthService;
use Leantime\Domain\Setting\Repositories\Setting as SettingRepository;
use Leantime\Domain\Timesheets\Repositories\Timesheets as TimesheetRepository;
use Leantime\Core\Template;
use Leantime\Core\UI\Template;

/**
* TimeTable controller.
Expand Down Expand Up @@ -54,40 +55,22 @@ public function post(): Response
if (!AuthService::userIsAtLeast(Roles::$editor)) {
return $this->template->displayJson(['Error' => 'Not Authorized'], 403);
}
$jsonPayload = json_decode(file_get_contents('php://input'), true);
if (isset($jsonPayload['action']) && $jsonPayload['action'] === 'delete') {
$timesheetId = $jsonPayload['timesheetId'];
if ($timesheetId) {
try {
$this->timesheetRepository->deleteTime($timesheetId);
exit(json_encode(['status' => 'success']));
} catch (Exception $e) {
exit(json_encode(['status' => 'error', 'error' => $e->getMessage()]));
}
}
$redirectUrl = BASE_URL . '/TimeTable/TimeTable';
$actionHandler = new TimeTableActionHandler($this->timeTableService, $this->timesheetRepository);

if (isset($_POST['action'])) {
$redirectUrl = match ($_POST['action']) {
'adjustPeriod' => $actionHandler->adjustPeriod($_POST, $redirectUrl),
'saveTicket' => $actionHandler->saveTicket($_POST, $redirectUrl),
'deleteTicket' => tap(function () use ($actionHandler, $redirectUrl) {
$actionHandler->deleteTicket($_POST, $redirectUrl);
}, fn() => $redirectUrl)(),
'copyEntryForward' => $actionHandler->copyEntryForward($_POST, $redirectUrl),
default => $redirectUrl,
};
}

$timesheetId = isset($_POST['timesheet-id']) ? (int) $_POST['timesheet-id'] : 0;
$workDate = new CarbonImmutable($_POST['timesheet-date'], session('usersettings.timezone'));
$workDate = $workDate->setToDbTimezone();

$values = [
'timesheetId' => $_POST['timesheet-id'],
'userId' => session('userdata.id'),
'hours' => $_POST['timesheet-hours'],
'workDate' => $workDate,
'ticketId' => $_POST['timesheet-ticket-id'],
'description' => $_POST['timesheet-description'],
'kind' => 'GENERAL_BILLABLE',
];
$this->timeTableService->updateOrAddTimelogOnTicket($values, $timesheetId);

$redirectUrl = BASE_URL . '/TimeTable/TimeTable';
if (isset($_GET['offset'])) {
$redirectUrl = $redirectUrl . '?offset=' . $_GET['offset'];
}

return Frontcontroller::redirect($redirectUrl);
return Frontcontroller::redirect($redirectUrl);
}

/**
Expand All @@ -99,41 +82,76 @@ public function post(): Response
*/
public function get(): Response
{
// Filters for the sql select
$userIdForFilter = null;
$searchTermForFilter = null;
$now = CarbonImmutable::now();
$ticketCacheExpiration = $this->settings->getSetting('itk-leantime-timetable.ticketCacheExpiration') ?? 1200;

if (isset($_GET['searchTerm'])) {
$searchTerm = $_GET['searchTerm'];
}
try {
if (isset($_GET['fromDate']) && $_GET['fromDate'] !== '') {
if ($_GET['fromDate'][0] === '+' || $_GET['fromDate'][0] === '-') {
// If relative date format
$fromDate = CarbonImmutable::now()->startOfDay()->modify($_GET['fromDate']);
} else {
// Try specific date format
$fromDate = CarbonImmutable::createFromFormat('Y-m-d', $_GET['fromDate'])->startOfDay();
if ($fromDate === false) {
// If 'Y-m-d' format fails, try 'd/m/Y' format
$fromDate = CarbonImmutable::createFromFormat('d/m/Y', $_GET['fromDate'])->startOfDay();
}
}
} else {
// Default to start of current week
$fromDate = CarbonImmutable::now()->startOfWeek()->startOfDay();
}

if (isset($_GET['offset'])) {
// Multiply offset by 7 days.
if ((int) $_GET['offset'] > 0) {
$now = $now->addDays((int) $_GET['offset'] * 7);
if (isset($_GET['toDate']) && $_GET['toDate'] !== '') {
if ($_GET['toDate'][0] === '+' || $_GET['toDate'][0] === '-') {
// If relative date format
$toDate = CarbonImmutable::now()->startOfDay()->modify($_GET['toDate']);
} else {
// Try specific date format
$toDate = CarbonImmutable::createFromFormat('Y-m-d', $_GET['toDate'])->startOfDay();
if ($toDate === false) {
// If 'Y-m-d' format fails, try 'd/m/Y' format
$toDate = CarbonImmutable::createFromFormat('d/m/Y', $_GET['toDate'])->startOfDay();
}
}
} else {
$now = $now->subDays(abs((int) $_GET['offset']) * 7);
// Default to end of current week
$toDate = CarbonImmutable::now()->endOfWeek()->startOfDay();
}
}
if (isset($_GET['searchTerm']) && $_GET['searchTerm'] !== '') {
$searchTermForFilter = $_GET['searchTerm'];
} catch (\Exception $e) {
// Log error
Log::error($e);

// Assign fallback date span
$fromDate = CarbonImmutable::now()->startOfWeek()->startOfDay();
$toDate = CarbonImmutable::now()->endOfWeek()->startOfDay();
}

$weekStartDateDb = $now->startOfWeek()->setToDbTimezone();
$weekEndDateDb = $now->endOfWeek()->setToDbTimezone();
$weekStartDateFrontend = $now->startOfWeek()->setToUserTimezone();
$weekStartDateDb = $fromDate->setToDbTimezone();
$weekEndDateDb = $toDate->setToDbTimezone();

$this->template->assign('currentSearchTerm', $searchTermForFilter);

$days = explode(',', $this->language->__('language.dayNames'));
// Make the first day of week monday, by shifting sunday to the back of the array.
$days = explode(',', mb_strtolower($this->language->__('language.dayNames')));
$days[] = array_shift($days);

$weekDates = [];
foreach ($days as $key => $day) {
$weekDates[$key] = $weekStartDateFrontend->addDays($key);
$dateIterator = $fromDate->setToUserTimezone()->copy();

while ($dateIterator <= $toDate) {
$dayOfWeek = strtolower($dateIterator->locale(session('usersettings.language'))->dayName);

// If the day is a part of the week
if (in_array($dayOfWeek, $days)) {
$weekDates[$dateIterator->format('d-m-Y')] = $dateIterator->copy();
}

// Move on to the next day
$dateIterator = $dateIterator->addDay();
}

$relevantTicketIds = $this->timeTableService->getUniqueTicketIds($weekStartDateDb, $weekEndDateDb);

$timesheetsByTicket = [];
Expand All @@ -146,26 +164,26 @@ public function get(): Response
$timesheetsSortedByWeekdate = [];
foreach ($weekDates as $weekDate) {
$timesheetsByTicketAndDate = $this->timeTableService->getTimesheetByTicketIdAndWorkDate($ticket['ticketId'], $weekDate->setToDbTimezone(), $searchTermForFilter);

$timesheetsSortedByWeekdate[$weekDate->format('Y-m-d')] = $timesheetsByTicketAndDate;
if (count($timesheetsByTicketAndDate) > 0) {
$timesheetsSortedByWeekdate['ticketTitle'] = $timesheetsByTicketAndDate[0]['headline'];
$timesheetsSortedByWeekdate['ticketLink'] = '?showTicketModal=' . $timesheetsByTicketAndDate[0]['ticketId'] . '#/tickets/showTicket/' . $timesheetsByTicketAndDate[0]['ticketId'];
$timesheetsSortedByWeekdate['projectName'] = $timesheetsByTicketAndDate[0]['name'];
$timesheetsSortedByWeekdate['ticketType'] = $timesheetsByTicketAndDate[0]['ticketType'];
$timesheetsSortedByWeekdate['ticketId'] = $timesheetsByTicketAndDate[0]['ticketId'];
}
}

$timesheetsByTicket[$ticket['ticketId']] = $timesheetsSortedByWeekdate;
}
// All tickets assignet to the template
$this->template->assign('userId', session('userdata.id'));
// All tickets assigned to the template
$this->template->assign('ticketIds', implode(',', $ticketIds));
$this->template->assign('timesheetsByTicket', $timesheetsByTicket);
$this->template->assign('weekDays', $days);
$this->template->assign('weekDates', $weekDates);
$this->template->assign('ticketCacheExpiration', $ticketCacheExpiration);

$this->template->assign('fromDate', $fromDate);
$this->template->assign('toDate', $toDate);
return $this->template->display('TimeTable.timetable');
}
}
Loading
Loading