Skip to content

Commit

Permalink
Merge pull request #2769 from Leantime/tickets-kanban-htmx
Browse files Browse the repository at this point in the history
Tickets kanban htmx
  • Loading branch information
marcelfolaron authored Nov 20, 2024
2 parents 43950bd + efec3d7 commit 05b5a3e
Show file tree
Hide file tree
Showing 10 changed files with 505 additions and 523 deletions.
10 changes: 0 additions & 10 deletions app/Domain/Tickets/Controllers/ShowKanban.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,14 @@

class ShowKanban extends Controller
{
private ProjectService $projectService;

private TicketService $ticketService;

private SprintService $sprintService;

private TimesheetService $timesheetService;

public function init(
ProjectService $projectService,
TicketService $ticketService,
SprintService $sprintService,
TimesheetService $timesheetService
): void {
$this->projectService = $projectService;
$this->ticketService = $ticketService;
$this->sprintService = $sprintService;
$this->timesheetService = $timesheetService;

session(['lastPage' => CURRENT_URL]);
session(['lastTicketView' => 'kanban']);
Expand Down
52 changes: 52 additions & 0 deletions app/Domain/Tickets/Hxcontrollers/TicketColumn.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace Leantime\Domain\Tickets\Hxcontrollers;

use Leantime\Core\Controller\HtmxController;
use Leantime\Domain\Tickets\Services\Tickets;
use Leantime\Domain\Timesheets\Services\Timesheets;
use Leantime\Domain\Projects\Services\Projects;


class TicketColumn extends HtmxController
{
protected static string $view = 'tickets::components.ticket-column';

private Tickets $ticketService;
private Projects $projectService;
private Timesheets $timesheetService;
/**
* Controller constructor
*
* @param Timesheets $timesheetService
*/
public function init(Tickets $ticketService, Projects $projectService, Timesheets $timesheetService): void
{
$this->ticketService = $ticketService;
$this->projectService = $projectService;
$this->timesheetService = $timesheetService;
}

public function get($params): void
{
// $ticketId = (int) ($params['id']);
$statusKey = (int) ($params['status']);
$allTickets = $this->ticketService->getAll(['status' => $statusKey]);
$ticketTypeIcons = $this->ticketService->getTypeIcons();
$priorities = $this->ticketService->getPriorityLabels();
$efforts = $this->ticketService->getEffortLabels();
$milestones = $this->ticketService->getAllMilestones(['sprint' => '', 'type' => 'milestone', 'currentProject' => session('currentProject')]);
$users = $this->projectService->getUsersAssignedToProject(session('currentProject'));
$onTheClock = $this->timesheetService->isClocked(session('userdata.id'));

$this->tpl->assign('onTheClock', $onTheClock);
$this->tpl->assign("efforts", $efforts);
$this->tpl->assign("milestones", $milestones);
$this->tpl->assign("users", $users);
$this->tpl->assign("allTickets", $allTickets);
$this->tpl->assign("ticketTypeIcons", $ticketTypeIcons);
$this->tpl->assign("priorities", $priorities);
$this->tpl->assign("statusKey", $statusKey);

}
}
1 change: 0 additions & 1 deletion app/Domain/Tickets/Services/Tickets.php
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ public function getTicketTypes(): array
*/
public function getPriorityLabels(): array
{

return $this->ticketRepository->priority;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@props(['ticket', 'milestones'])

<div class="dropdown ticketDropdown milestoneDropdown colorized show firstDropdown">
@php
$milestoneLabelText = '<span class="text">';
if ($ticket['milestoneid'] != '' && $ticket['milestoneid'] != 0) {
$milestoneLabelText .= $ticket['milestoneHeadline'];
} else {
$milestoneLabelText .= __('label.no_milestone');
}
$milestoneLabelText .= '</span>&nbsp;<i class="fa fa-caret-down" aria-hidden="true"></i>';
@endphp

<x-global::actions.dropdown
:label-text="$milestoneLabelText"
contentRole="link"
position="bottom"
align="start"
>
<x-slot:menu>
<li class="nav-header border">{{ __('dropdown.choose_milestone') }}</li>
<x-global::actions.dropdown.item
style="background-color: #b0b0b0"
href="javascript:void(0);"
data-label="{{ __('label.no_milestone') }}"
data-value="{{ $ticket['id'] . '_0_#b0b0b0' }}"
>
{{ __('label.no_milestone') }}
</x-global::actions.dropdown.item>

@foreach ($milestones as $milestone)
<x-global::actions.dropdown.item
href="javascript:void(0);"
data-label="{{ $milestone->headline }}"
data-value="{{ $ticket['id'] . '_' . $milestone->id . '_' . $milestone->tags }}"
id="ticketMilestoneChange{{ $ticket['id'] . $milestone->id }}"
style="background-color: {{ $milestone->tags }}"
>
{{ $milestone->headline }}
</x-global::actions.dropdown.item>
@endforeach
</x-slot:menu>
</x-global::actions.dropdown>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</x-slot:label-text>

<x-global::forms.select.option :value="''">
{{ __('label.priority_not_defined') }}
{!! __('label.priority_not_defined') !!}
</x-global::forms.select.option>

@foreach ($priorities as $priorityKey => $priorityValue)
Expand All @@ -22,7 +22,7 @@
:selected="strtolower($priorityKey) == strtolower( $ticket->priority ?? '') ? 'true' : 'false'">

<span class="priority-text-{{ $priorityKey }} ">
<x-global::content.icon icon="local_fire_department" /> {{ $priorityValue }}
<x-global::content.icon icon="local_fire_department" /> {!! $priorityValue !!}
</span>

</x-global::forms.select.option>
Expand Down
159 changes: 159 additions & 0 deletions app/Domain/Tickets/Templates/components/ticket-column-card.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
@props([
'ticket' => [],
'statusKey' => '',
'todoTypeIcons' => [],
'priorities' => [],
'efforts' => [],
'milestones' => [],
'users' => [],
'onTheClock' => false,
])

@if ($ticket['status'] == $statusKey)
<div class="ticketBox moveable container priority-border-{{ $ticket['priority'] }}" id="ticket_{{ $ticket['id'] }}">
<div class="row">
<div class="col-md-12">
@include('tickets::includes.ticketsubmenu', [
'ticket' => $ticket,
'onTheClock' => $onTheClock,
])

@if ($ticket['dependingTicketId'] > 0)
<small>
<a href="#/tickets/showTicket/{{ $ticket['dependingTicketId'] }}"
class="form-modal">{{ $ticket['parentHeadline'] }}</a>
</small>
//
@endif
@php
$type = strtolower($ticket['type']);
@endphp
@isset($todoTypeIcons[$type])
<small>
<i class="fa {{ $todoTypeIcons[$type] }}"></i>
{!!__('label.' . $type) !!}
</small>
@endisset
<small>#{{ $ticket['id'] }}</small>

<div class="kanbanCardContent">
<h4>
<a href="#/tickets/showTicket/{{ $ticket['id'] }}">{{ $ticket['headline'] }}</a>
</h4>
<div class="kanbanContent" style="margin-bottom: 20px">
{!! $ticket['description'] !!}
</div>
</div>

@if ($ticket['dateToFinish'] != '0000-00-00 00:00:00' && $ticket['dateToFinish'] != '1969-12-31 00:00:00')
<x-global::forms.text-input type="text" name="date" value="{!! format($ticket['dateToFinish'])->date() !!}"
title="{{ __('label.due') }}" class="duedates secretInput" style="margin-left: 0px;"
data-id="{{ $ticket['id'] }}" leadingVisual="{!! __('label.due_icon') !!}" />
@endif
</div>
</div>

<div class="clearfix" style="padding-bottom: 8px;"></div>

<div class="timerContainer" id="timerContainer-{{ $ticket['id'] }}">
<x-tickets::milestone-dropdown :ticket="$ticket" :milestones="$milestones" />

@if ($ticket['storypoints'] != '' && $ticket['storypoints'] > 0)
<x-tickets::effort-select :ticket="$ticket" :efforts="$efforts" />
@endif

<x-tickets::priority-select :ticket="$ticket" :priorities="$priorities" />

<div class="dropdown ticketDropdown userDropdown noBg show right lastDropdown dropRight">
@php
// Determine the label text dynamically
$userLabelText = '<span class="text">';
if ($ticket['editorFirstname'] != '') {
$userLabelText .=
"<span id='userImage" .
$ticket['id'] .
"'><img src='" .
BASE_URL .
'/api/users?profileImage=' .
$ticket['editorId'] .
"' width='25' style='vertical-align: middle;'/></span>";
} else {
$userLabelText .=
"<span id='userImage" .
$ticket['id'] .
"'><img src='" .
BASE_URL .
"/api/users?profileImage=false' width='25' style='vertical-align: middle;'/></span>";
}
$userLabelText .= '</span>';
@endphp

<!-- Dropdown Component -->
<x-global::actions.dropdown :label-text="$userLabelText" contentRole="link" position="bottom" align="start">
<!-- Dropdown Items -->
<x-slot:menu>
<li class="nav-header border">{{ $tpl->__('dropdown.choose_user') }}
</li>
@if (is_array($tpl->get('users')))
@foreach ($tpl->get('users') as $user)
<x-global::actions.dropdown.item href="javascript:void(0);"
data-label="{{ sprintf($tpl->__('text.full_name'), $tpl->escape($user['firstname']), $tpl->escape($user['lastname'])) }}"
data-value="{{ $ticket['id'] . '_' . $user['id'] . '_' . $user['profileId'] }}"
id="userStatusChange{{ $ticket['id'] . $user['id'] }}">
<img src="{{ BASE_URL }}/api/users?profileImage={{ $user['id'] }}"
width="25" style="vertical-align: middle; margin-right:5px;" />
{{ sprintf($tpl->__('text.full_name'), $tpl->escape($user['firstname']), $tpl->escape($user['lastname'])) }}
</x-global::actions.dropdown.item>
@endforeach
@endif
</x-slot:menu>
</x-global::actions.dropdown>

</div>
</div>

<div class="clearfix"></div>

@if ($ticket['commentCount'] > 0 || $ticket['subtaskCount'] > 0 || $ticket['tags'] != '')
<div class="row">
<div class="col-md-12 border-top" style="white-space: nowrap;">
@if ($ticket['commentCount'] > 0)
<a href="#/tickets/showTicket/{{ $ticket['id'] }}">
<span class="fa-regular fa-comments"></span>
{{ $ticket['commentCount'] }}
</a>&nbsp;
@endif

@if ($ticket['subtaskCount'] > 0)
<a id="subtaskLink_{{ $ticket['id'] }}" href="#/tickets/showTicket/{{ $ticket['id'] }}"
class="subtaskLineLink">
<span class="fa fa-diagram-successor"></span>
{{ $ticket['subtaskCount'] }}
</a>&nbsp;
@endif

@if ($ticket['tags'] != '')
@if ($ticket['tags'])
@php
$tagsArray = explode(',', $ticket['tags']);
@endphp
<a href="javascript:void(0);" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-tags" aria-hidden="true"></i> {{ count($tagsArray) }}
</a>
<ul class="dropdown-menu">
<li style="padding:10px">
<div class='tagsinput readonly'>
@foreach ($tagsArray as $tag)
<span class='tag'><span>{{ $tag }}</span></span>
@endforeach
</div>
</li>
</ul>
@endif
@endif
</div>
</div>
@endif
</div>
@endif
28 changes: 28 additions & 0 deletions app/Domain/Tickets/Templates/components/ticket-column.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@props([
'status' => '',
'statusKey' => '',
'allTickets' => [],
'ticketTypeIcons' => [],
'priorities' => [],
'efforts' => [],
'milestones' => [],
'users' => [],
'onTheClock' => false,
])
@if (!empty($status))
<div class="column justify-content-center align-items-center" hx-get="{{ BASE_URL }}/hx/tickets/ticketColumn/get?status={{ $status }}" hx-swap="outerHTML"
hx-trigger="load">
<x-global::elements.loader id="loadingthis" size="25px" />
</div>
@else
<div class="column">
<div class="contentInner status_{{ $statusKey }}">
@foreach ($allTickets as $ticket)
@if ($ticket['status'] == $statusKey)
<x-tickets::ticket-column-card :ticket="$ticket" :statusKey="$statusKey" :todoTypeIcons="$ticketTypeIcons"
:priorities="$priorities" :efforts="$efforts" :milestones="$milestones" :users="$users" :onTheClock="$onTheClock" />
@endif
@endforeach
</div>
</div>
@endif
Loading

0 comments on commit 05b5a3e

Please sign in to comment.