OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
@props([
|
||||
'milestone' => null,
|
||||
'noText' => false,
|
||||
'percentDone' => 0,
|
||||
'progressColor' => 'default'
|
||||
])
|
||||
|
||||
<div class="ticketBox fixed">
|
||||
<div class="row">
|
||||
<div class="col-md-8" style="margin-bottom:5px;">
|
||||
<strong><a href="<?=BASE_URL ?>/tickets/showKanban?milestone={{ $milestone->id }}" >{{ $milestone->headline }}</a></strong>
|
||||
</div>
|
||||
<div class="col-md-4 align-right">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@fragment('progress')
|
||||
|
||||
@if($noText === false || $noText === null)
|
||||
<div class="row progress-wrapper">
|
||||
<div class="col-md-7 percent-label">
|
||||
{{ __("label.due") }}
|
||||
<?php echo format($milestone->editTo )->date($tpl->__("text.no_date_defined")); ?>
|
||||
</div>
|
||||
<div class="col-md-5 percent-label" style="text-align:right">
|
||||
<?=sprintf($tpl->__("text.percent_complete"), format($percentDone)->decimal())?>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="progress" data-tippy-content="<?=sprintf($tpl->__("text.percent_complete"), format($percentDone)->decimal())?>">
|
||||
<div class="progress-bar progress-bar-success"
|
||||
role="progressbar"
|
||||
aria-valuenow="{{ $percentDone }}"
|
||||
aria-valuemin="0" aria-valuemax="100"
|
||||
style="width: {{ $percentDone }}%; {{ $progressColor !== 'default' ? ' background: #'.$progressColor.'; ' : '' }}">
|
||||
<span class="sr-only">{{ sprintf($tpl->__("text.percent_complete"), format($percentDone)->decimal()) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Idempotent init — a bare tippy('[data-tippy-content]') here re-instanced
|
||||
// every tooltip on the page each time a milestone card rendered (flashing
|
||||
// + duplicate header tooltips). initTooltips skips already-instanced els.
|
||||
window.leantime?.initTooltips?.();
|
||||
</script>
|
||||
@endfragment
|
||||
</div>
|
||||
@@ -0,0 +1,87 @@
|
||||
@php
|
||||
/**
|
||||
* Quick-add form partial for Kanban columns
|
||||
*
|
||||
* @var int $statusId - Status column ID
|
||||
* @var string|null $swimlaneKey - Swimlane identifier
|
||||
* @var bool $isEmpty - Whether column is empty
|
||||
* @var array|null $reopenState - Session flash data
|
||||
*/
|
||||
$isActive = !empty($reopenState)
|
||||
&& $reopenState['status'] == $statusId
|
||||
&& ($reopenState['swimlane'] ?? null) == ($swimlaneKey ?? null);
|
||||
|
||||
$savedHeadline = $isActive ? ($reopenState['headline'] ?? '') : '';
|
||||
$hasError = $isActive && !empty($reopenState['error']);
|
||||
@endphp
|
||||
|
||||
<div class="quickaddContainer tw-mb-s {{ $isEmpty ? 'quickaddContainer--empty' : '' }}" data-status="{{ $statusId }}" data-swimlane="{{ $swimlaneKey ?? '' }}">
|
||||
<a href="javascript:void(0);"
|
||||
class="quickAddLink {{ $isEmpty ? 'empty-state' : 'inline-add' }}"
|
||||
onclick="leantime.kanbanController.toggleQuickAdd(this)"
|
||||
aria-expanded="{{ $isActive ? 'true' : 'false' }}"
|
||||
aria-controls="quickadd-form-{{ $statusId }}-{{ $swimlaneKey ?? 'default' }}"
|
||||
style="{{ $isActive ? 'display:none;' : '' }}">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
<span>Add To-Do</span>
|
||||
</a>
|
||||
|
||||
<form method="post"
|
||||
class="quickAddForm {{ $isActive ? 'active' : '' }}"
|
||||
id="quickadd-form-{{ $statusId }}-{{ $swimlaneKey ?? 'default' }}"
|
||||
data-quickadd-form
|
||||
style="{{ $isActive ? '' : 'display:none;' }}"
|
||||
data-submitting="false">
|
||||
<input type="hidden" name="quickadd" value="1" />
|
||||
<input type="hidden" name="status" value="{{ $statusId }}" />
|
||||
<input type="hidden" name="swimlane" value="{{ $swimlaneKey ?? '' }}" />
|
||||
<input type="hidden" name="groupBy" value="{{ $currentGroupBy ?? '' }}" />
|
||||
<input type="hidden" name="milestone" value="{{ $searchCriteria['milestone'] ?? '' }}" />
|
||||
<input type="hidden" name="sprint" value="{{ session('currentSprint') ?? '' }}" />
|
||||
<input type="hidden" name="stay_open" value="0" data-stay-open-input />
|
||||
|
||||
@if (! empty($programBoard) && ! empty($availableProjects))
|
||||
{{-- Program board: a new task must belong to exactly one child project. --}}
|
||||
<div class="form-group">
|
||||
<select name="quickaddProjectId" class="form-control" required aria-label="{{ __('label.project') }}">
|
||||
<option value="">{{ __('label.project') }}…</option>
|
||||
@foreach ($availableProjects as $quickAddProjectId => $quickAddProjectName)
|
||||
<option value="{{ $quickAddProjectId }}">{{ $tpl->escape($quickAddProjectName) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="form-group">
|
||||
<label for="headline-{{ $statusId }}-{{ $swimlaneKey ?? 'default' }}" class="sr-only">Task name</label>
|
||||
<input type="text"
|
||||
name="headline"
|
||||
id="headline-{{ $statusId }}-{{ $swimlaneKey ?? 'default' }}"
|
||||
class="form-control quickAddInput {{ $hasError ? 'error' : '' }}"
|
||||
placeholder="What are you working on? ↵"
|
||||
value="{{ htmlspecialchars($savedHeadline) }}"
|
||||
{{ $isActive ? 'autofocus' : '' }}
|
||||
data-quickadd-input />
|
||||
|
||||
@if ($hasError)
|
||||
<div class="error-message" role="alert">{{ htmlspecialchars($reopenState['error']) }}</div>
|
||||
@endif
|
||||
|
||||
<div id="quick-add-help-{{ $statusId }}-{{ $swimlaneKey ?? 'default' }}" class="sr-only">
|
||||
Press Enter to save and close. Press Shift plus Enter to save and add another task. Press Escape to cancel.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="formButtonContainer">
|
||||
<x-global::forms.button inputType="submit" contentRole="primary" onclick="this.closest('form').dataset.submitting = 'true'; this.closest('form').querySelector('[data-stay-open-input]').value = '0';">Save</x-global::forms.button>
|
||||
<x-global::forms.button inputType="button" contentRole="secondary"
|
||||
onclick="leantime.kanbanController.toggleQuickAdd(this.closest('.quickaddContainer').querySelector('.quickAddLink'))">
|
||||
Cancel
|
||||
</x-global::forms.button>
|
||||
<i class="fa fa-circle-question"
|
||||
data-tippy-content="<strong>Keyboard Shortcuts:</strong><br>Enter: Save and close<br>Shift+Enter: Save and add another<br>Esc: Cancel"
|
||||
tabindex="0"
|
||||
aria-label="Keyboard shortcuts help"></i>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
163
app/Domain/Tickets/Templates/partials/subtasks.blade.php
Normal file
163
app/Domain/Tickets/Templates/partials/subtasks.blade.php
Normal file
@@ -0,0 +1,163 @@
|
||||
|
||||
<ul class="sortableTicketList" style="margin-bottom:120px;">
|
||||
<li class="">
|
||||
<a href="javascript:void(0);" class="quickAddLink" id="subticket_new_link" onclick="jQuery('#subticket_new').toggle('fast', function() {jQuery(this).find('input[name=headline]').focus();}); jQuery(this).toggle('fast');"><i class="fas fa-plus-circle"></i> {{ __("links.add_task") }}</a>
|
||||
<div class="ticketBox hideOnLoad" id="subticket_new" >
|
||||
|
||||
<form method="post" class="form-group"
|
||||
hx-post="{{ BASE_URL }}/tickets/subtasks/save?ticketId={{ $ticket->id }}"
|
||||
hx-indicator=".htmx-indicator-small"
|
||||
hx-target="#ticketSubtasks">
|
||||
<input type="hidden" value="new" name="subtaskId" />
|
||||
<input type="hidden" value="1" name="subtaskSave" />
|
||||
<input name="headline" type="text" title="{{ __("label.headline") }}" style="width:100%" placeholder="{{ __("input.placeholders.what_are_you_working_on") }}" />
|
||||
<input type="submit" value="{{ __("buttons.save") }}" name="quickadd" />
|
||||
<div class="htmx-indicator-small">
|
||||
<x-global::loader id="loadingthis" size="25px" />
|
||||
</div>
|
||||
<input type="hidden" name="dateToFinish" id="dateToFinish" value="" />
|
||||
<input type="hidden" name="status" value="3" />
|
||||
<input type="hidden" name="sprint" value="{{ session("currentSprint") }}" />
|
||||
<x-global::forms.button tag="a" link="javascript:void(0);" onclick="jQuery('#subticket_new').toggle('fast'); jQuery('#subticket_new_link').toggle('fast');" contentRole="tertiary">
|
||||
{{ __("links.cancel") }}
|
||||
</x-global::forms.button>
|
||||
</form>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
|
||||
@php
|
||||
|
||||
$sumPlanHours = 0;
|
||||
$sumEstHours = 0;
|
||||
|
||||
@endphp
|
||||
|
||||
|
||||
@foreach ($ticketSubtasks as $subticket)
|
||||
|
||||
@php
|
||||
$sumPlanHours = $sumPlanHours + $subticket['planHours'];
|
||||
$sumEstHours = $sumEstHours + $subticket['hourRemaining'];
|
||||
|
||||
if ($subticket['dateToFinish'] == "0000-00-00 00:00:00" || $subticket['dateToFinish'] == "1969-12-31 00:00:00") {
|
||||
$date = $tpl->__("text.anytime");
|
||||
} else {
|
||||
$date = format($subticket['dateToFinish'])->date();
|
||||
}
|
||||
|
||||
@endphp
|
||||
|
||||
<li class="ui-state-default" id="ticket_{{ $subticket['id'] }}" >
|
||||
<div class="ticketBox fixed priority-border-{{ $subticket['priority'] }}" data-val="{{ $subticket['id'] }}" >
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12" style="padding:0 15px;">
|
||||
@if($login::userIsAtLeast($roles::$editor))
|
||||
<div class="inlineDropDownContainer" >
|
||||
<a href="javascript:void(0)" class="dropdown-toggle ticketDropDown" data-toggle="dropdown">
|
||||
<i class="fa fa-ellipsis-v" aria-hidden="true"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="javascript:void(0);" hx-delete="{{ BASE_URL }}/tickets/subtasks/delete?ticketId={{ $subticket["id"] }}&parentTicket={{ $ticket->id }}" hx-target="#ticketSubtasks" class="delete"><i class="fa fa-trash"></i> {{ __("links.delete_todo") }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<a href="#/tickets/showTicket/{{ $subticket['id'] }}">{{ $subticket['headline'] }}</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-9" style="padding:0 15px;">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
{{ __("label.due") }}<input type="text" title="{{ __("label.due") }}" value="{{ $date }}" class="duedates secretInput quickDueDates" data-id="{{ $subticket['id'] }}" name="date" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{{ __("label.planned_hours") }}<input type="text" value="{{ $subticket['planHours'] }}" name="planHours" data-label="planHours-{{ $subticket['id'] }}" class="small-input secretInput asyncInputUpdate" style="width:40px"/>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{{ __("label.estimated_hours_remaining") }}<input type="text" value="{{ $subticket['hourRemaining'] }}" name="hourRemaining" data-label="hourRemaining-{{ $subticket['id'] }}" class="small-input secretInput asyncInputUpdate" style="width:40px"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3" style="padding-top:3px;" >
|
||||
<div class="right">
|
||||
<div class="dropdown ticketDropdown effortDropdown show">
|
||||
<a class="dropdown-toggle f-left label-default effort" href="javascript:void(0);" role="button" id="effortDropdownMenuLink{{ $subticket['id'] }}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="text">@if ($subticket['storypoints'] != '' && $subticket['storypoints'] > 0 && isset($efforts[$subticket['storypoints']]))
|
||||
{{ $efforts[$subticket['storypoints']] }}
|
||||
@else
|
||||
{{ __("label.story_points_unkown") }}
|
||||
@endif
|
||||
</span>
|
||||
<i class="fa fa-caret-down" aria-hidden="true"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="effortDropdownMenuLink{{ $subticket['id'] }}">
|
||||
<li class="nav-header border">{{ __("dropdown.how_big_todo") }}</li>
|
||||
@foreach($efforts as $effortKey => $effortValue)
|
||||
<li class='dropdown-item'>
|
||||
<a href='javascript:void(0);' data-value='{{ $subticket['id'] }}_{{ $effortKey }}' id='ticketEffortChange{{ $subticket['id'] . $effortKey }}'> {{ $effortValue }}</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@php
|
||||
if (isset($statusLabels[$subticket['status']])) {
|
||||
$class = $statusLabels[$subticket['status']]["class"];
|
||||
$name = $statusLabels[$subticket['status']]["name"];
|
||||
} else {
|
||||
$class = 'label-important';
|
||||
$name = 'new';
|
||||
}
|
||||
@endphp
|
||||
<div class="dropdown ticketDropdown statusDropdown colorized show">
|
||||
<a class="dropdown-toggle f-left status {{ $class }}" href="javascript:void(0);" role="button" id="statusDropdownMenuLink{{ $subticket['id'] }}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="text">{{$name }}
|
||||
</span>
|
||||
<i class="fa fa-caret-down" aria-hidden="true"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="statusDropdownMenuLink{{ $subticket['id'] }}">
|
||||
<li class="nav-header border">{{ __('dropdown.choose_status') }}</li>
|
||||
|
||||
@foreach ($statusLabels as $key => $label)
|
||||
<li class='dropdown-item'>
|
||||
<a href='javascript:void(0);' class='{{ $label["class"] }}' data-label='{{ $label["name"] }}' data-value='{{ $subticket['id'] }}_{{ $key }}_{{ $label["class"] }}' id='ticketStatusChange{{ $subticket['id'] . $key }}' >{{ $label["name"] }}</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
jQuery(document).ready(function(){
|
||||
<?php if ($login::userIsAtLeast($roles::$editor)) { ?>
|
||||
|
||||
leantime.ticketsController.initAsyncInputChange();
|
||||
leantime.ticketsController.initDueDateTimePickers();
|
||||
|
||||
leantime.ticketsController.initEffortDropdown();
|
||||
leantime.ticketsController.initStatusDropdown();
|
||||
|
||||
<?php } else { ?>
|
||||
|
||||
leantime.authController.makeInputReadonly(".nyroModalCont");
|
||||
|
||||
<?php } ?>
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
139
app/Domain/Tickets/Templates/partials/ticketCard.blade.php
Normal file
139
app/Domain/Tickets/Templates/partials/ticketCard.blade.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<div class="ticketBox fixed priority-border-{{ $row['priority'] }}" data-val="{{ $row['id'] }}">
|
||||
<div class="row">
|
||||
<div class="col-md-8 titleContainer">
|
||||
@if($cardType == "full")
|
||||
<small>{{ $row['projectName'] }}</small><br />
|
||||
@if($row['dependingTicketId'] > 0)
|
||||
<a href="#/tickets/showTicket/{{ $row['dependingTicketId'] }}">{{ $row['parentHeadline'] }}</a> //
|
||||
@endif
|
||||
@endif
|
||||
<strong><a href="#/tickets/showTicket/{{ $row['id'] }}" >{{ $row['headline'] }}</a></strong>
|
||||
|
||||
</div>
|
||||
<div class="col-md-4 timerContainer" style="padding:5px 15px;" id="timerContainer-{{ $row['id'] }}">
|
||||
|
||||
@include("tickets::partials.ticketsubmenu", ["ticket" => $row, "onTheClock" => $onTheClock])
|
||||
@if($cardType == "full")
|
||||
<div class="scheduler pull-right">
|
||||
@if( $row['editFrom'] != "0000-00-00 00:00:00" && $row['editFrom'] != "1969-12-31 00:00:00")
|
||||
<i class="fa-solid fa-calendar-check infoIcon tw-mr-xs" style="color:var(--accent2)" data-tippy-content="{{ __('text.schedule_to_start_on') }} {{ format($row['editFrom'])->date() }}"></i>
|
||||
@else
|
||||
<i class="fa-regular fa-calendar-xmark infoIcon tw-mr-xs" data-tippy-content="{{ __('text.not_scheduled_drag_ai') }}"></i>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-4" style="padding:0 15px;">
|
||||
@if($cardType == "full")
|
||||
<i class="fa-solid fa-business-time infoIcon" data-tippy-content=" {{ __("label.due") }}"></i>
|
||||
<input type="text" title="{{ __("label.due") }}" value="{{ format($row['dateToFinish'])->date(__("text.anytime")) }}" class="duedates secretInput" style="margin-left:0px;" data-id="{{ $row['id'] }}" name="date" />
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="col-md-8 dropdownContainer" style="padding-top:5px;">
|
||||
<div class="dropdown ticketDropdown statusDropdown colorized show right ">
|
||||
<a class="dropdown-toggle f-left status {{ $statusLabels[$row['status']]["class"] }}" href="javascript:void(0);" role="button" id="statusDropdownMenuLink{{ $row['id'] }}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="text">
|
||||
@if(isset($statusLabels[$row['status']]))
|
||||
{{ $statusLabels[$row['status']]["name"] }}
|
||||
@else
|
||||
unknown
|
||||
@endif
|
||||
</span>
|
||||
<i class="fa fa-caret-down" aria-hidden="true"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right" aria-labelledby="statusDropdownMenuLink{{ $row['id'] }}">
|
||||
<li class="nav-header border">{{ __("dropdown.choose_status") }}</li>
|
||||
|
||||
@foreach ($statusLabels as $key => $label)
|
||||
<li class='dropdown-item'>
|
||||
<a href='javascript:void(0);'
|
||||
class='{{ $label["class"] }}'
|
||||
data-label='{{ $label["name"] }}'
|
||||
data-value='{{ $row['id'] }}_{{ $key }}_{{ $label["class"] }}'
|
||||
id='ticketStatusChange{{$row['id'] . $key }}'>
|
||||
{{ $label["name"] }}
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<?php /*
|
||||
<div class="dropdown ticketDropdown effortDropdown show right">
|
||||
<a class="dropdown-toggle f-left label-default effort" href="javascript:void(0);" role="button" id="effortDropdownMenuLink{{ $row['id'] }}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="text">
|
||||
@if ($row['storypoints'] != '' && $row['storypoints'] > 0)
|
||||
{{ $efforts["" . $row['storypoints']] ?? $row['storypoints'] }}
|
||||
@else
|
||||
{{ __("label.story_points_unkown") }}
|
||||
@endif
|
||||
</span>
|
||||
<i class="fa fa-caret-down" aria-hidden="true"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="effortDropdownMenuLink{{ $row['id'] }}">
|
||||
<li class="nav-header border">{{ __("dropdown.how_big_todo") }}</li>
|
||||
@foreach($efforts as $effortKey => $effortValue)
|
||||
<li class='dropdown-item'>
|
||||
<a href='javascript:void(0);'
|
||||
data-value='{{ $row['id'] . "_" . $effortKey }}'
|
||||
id='ticketEffortChange{{ $row['id'] . $effortKey }}'>
|
||||
{{ $effortValue }}
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
*/ ?>
|
||||
@if($cardType == "full")
|
||||
<div class="dropdown ticketDropdown milestoneDropdown colorized show right tw-mr-sm">
|
||||
<a style="background-color:{{ $row['milestoneColor'] }}"
|
||||
class="dropdown-toggle f-left label-default milestone"
|
||||
href="javascript:void(0);"
|
||||
role="button" id="milestoneDropdownMenuLink{{ $row['id'] }}"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
<span class="text">
|
||||
@if($row['milestoneid'] != "" && $row['milestoneid'] != 0)
|
||||
{{ $row['milestoneHeadline'] }}
|
||||
@else
|
||||
{{ __("label.no_milestone") }}
|
||||
@endif
|
||||
</span>
|
||||
<i class="fa fa-caret-down" aria-hidden="true"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right" aria-labelledby="milestoneDropdownMenuLink{{ $row['id'] }}">
|
||||
<li class="nav-header border">{{ __("dropdown.choose_milestone") }}</li>
|
||||
<li class='dropdown-item'>
|
||||
<a style='background-color:#b0b0b0'
|
||||
href='javascript:void(0);'
|
||||
data-label="{{__("label.no_milestone") }}"
|
||||
data-value='{{ $row['id'] }}_0_#b0b0b0'>
|
||||
{{ __("label.no_milestone") }}
|
||||
</a>
|
||||
</li>
|
||||
@if(isset($milestones))
|
||||
@foreach($milestones as $milestone)
|
||||
@if(is_object($milestone))
|
||||
<li class='dropdown-item'>
|
||||
<a href='javascript:void(0);'
|
||||
data-label='{{ $milestone->headline }}'
|
||||
data-value='{{ $row['id'] }}_{{ $milestone->id }}_{{ $milestone->tags }}'
|
||||
id='ticketMilestoneChange{{ $row['id'] . $milestone->id }}'
|
||||
style='background-color:{{ $milestone->tags }}'>
|
||||
{{ $milestone->headline }}
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
@props([
|
||||
'ticket' => false,
|
||||
'onTheClock' => false,
|
||||
'allowSubtaskCreation' => false
|
||||
])
|
||||
|
||||
@if ($login::userIsAtLeast(\Leantime\Domain\Auth\Models\Roles::$editor))
|
||||
|
||||
<div class="inlineDropDownContainer" style="float:right;">
|
||||
|
||||
<a href="javascript:void(0);" class="dropdown-toggle ticketDropDown" data-toggle="dropdown">
|
||||
<i class="fa fa-ellipsis-v" aria-hidden="true"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="nav-header">{{ __("subtitles.todo") }}</li>
|
||||
@dispatchEvent("beforeShowTicket", ["ticket"=>$ticket])
|
||||
<li><a href="#/tickets/showTicket/{{ $ticket["id"] }}" class=''><i class="fa fa-edit"></i> {{ __("links.edit_todo") }}</a></li>
|
||||
@dispatchEvent("beforeMoveTicket", ["ticket"=>$ticket])
|
||||
<li><a href="#/tickets/moveTicket/{{ $ticket["id"] }}" class=""><i class="fa-solid fa-arrow-right-arrow-left"></i> {{ __("links.move_todo") }}</a></li>
|
||||
@if($allowSubtaskCreation)
|
||||
<li><a href="javascript:void(0);" onclick="jQuery('#subtask-form-{{$ticket['id']}}').toggle();"
|
||||
class="add-subtask-link">
|
||||
<i class="fa-solid fa-diagram-predecessor"></i> Add Subtask</a></li>
|
||||
@endif
|
||||
@dispatchEvent("beforeDeleteTicket", ["ticket"=>$ticket])
|
||||
<li><a href="#/tickets/delTicket/{{ $ticket["id"] }}" class="delete"><i class="fa fa-trash"></i> {{ __("links.delete_todo") }}</a></li>
|
||||
|
||||
|
||||
@dispatchEvent("submenuSection", ["ticket"=>$ticket])
|
||||
|
||||
<li class="nav-header border">{{ __("subtitles.track_time") }}</li>
|
||||
@dispatchEvent("beforeTimer", ["ticket"=>$ticket])
|
||||
<li class="timerContainer tw-px-[10px]">
|
||||
@include('tickets::partials.timerButton', ['parentTicketId' => $ticket['id'], 'onTheClock' => $onTheClock, 'style'=> 'full'])
|
||||
</li>
|
||||
@dispatchEvent("end")
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@endif
|
||||
78
app/Domain/Tickets/Templates/partials/timerButton.blade.php
Normal file
78
app/Domain/Tickets/Templates/partials/timerButton.blade.php
Normal file
@@ -0,0 +1,78 @@
|
||||
@props([
|
||||
'parentTicketId' => false,
|
||||
'onTheClock' => false,
|
||||
'style' => 'simple' //simple just the button, full witrh button text
|
||||
])
|
||||
|
||||
<div id="timer-button-container-{{ $parentTicketId }}"
|
||||
hx-get="{{BASE_URL}}/tickets/timerButton/get-status-button/{{ $parentTicketId }}"
|
||||
hx-trigger="timerUpdate from:body"
|
||||
hx-swap="outerHTML"
|
||||
|
||||
class="tw-relative timerContainer">
|
||||
|
||||
@if ($onTheClock === false)
|
||||
<a href="javascript:void(0);" data-value="{{ $parentTicketId }}"
|
||||
hx-patch="{{ BASE_URL }}/hx/timesheets/stopwatch/start-timer/"
|
||||
hx-target="#timerHeadMenu"
|
||||
hx-swap="outerHTML"
|
||||
onclick="this.classList.add('starting');"
|
||||
hx-vals='{"ticketId": "{{ $parentTicketId }}", "action":"start"}'
|
||||
data-tippy-content="{{ __("links.start_work") }}">
|
||||
|
||||
<span class="fa-regular fa-circle-play" style="font-size:18px; padding-top:3px;"></span>
|
||||
|
||||
@if($style=="full")
|
||||
{{ __("links.start_work") }}
|
||||
@endif
|
||||
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if ($onTheClock !== false && $onTheClock["id"] == $parentTicketId)
|
||||
<a href="javascript:void(0);" data-value="{{ $parentTicketId }}"
|
||||
hx-trigger="click delay:500ms"
|
||||
hx-patch="{{ BASE_URL }}/hx/timesheets/stopwatch/stop-timer/"
|
||||
hx-target="#timerHeadMenu"
|
||||
hx-vals='{"ticketId": "{{ $parentTicketId }}", "action":"stop"}'
|
||||
hx-swap="outerHTML"
|
||||
onclick="this.classList.add('stopped');"
|
||||
data-tippy-content="@if (is_array($onTheClock) == true) {!! strip_tags(sprintf(__("links.stop_work_started_at"), dtHelper()::createFromTimestamp($onTheClock["since"], 'UTC')->setToUserTimezone()->format(__("language.timeformat")))) !!} @else {!! strip_tags(sprintf(__("links.stop_work_started_at"), dtHelper()::now()->setToUserTimezone()->format(__("language.timeformat")))) !!} @endif"
|
||||
>
|
||||
|
||||
|
||||
<span class="fa-regular fa-circle-stop" style="font-size:18px; padding-top:3px;"></span>
|
||||
@if($style=="full")
|
||||
@if (is_array($onTheClock) == true)
|
||||
{!! sprintf(__("links.stop_work_started_at"), dtHelper()::createFromTimestamp($onTheClock["since"], 'UTC')->setToUserTimezone()->format(__("language.timeformat"))) !!}
|
||||
@else
|
||||
{!! sprintf(__("links.stop_work_started_at"), dtHelper()::now()->setToUserTimezone()->format(__("language.timeformat"))) !!}
|
||||
@endif
|
||||
@endif
|
||||
|
||||
<!-- These elements will be added dynamically when the timer is stopped -->
|
||||
<div class="success-circle"></div>
|
||||
<div class="particles-container">
|
||||
<div class="particle particle-1"></div>
|
||||
<div class="particle particle-2"></div>
|
||||
<div class="particle particle-3"></div>
|
||||
<div class="particle particle-4"></div>
|
||||
<div class="particle particle-5"></div>
|
||||
<div class="particle particle-6"></div>
|
||||
<div class="particle particle-7"></div>
|
||||
<div class="particle particle-8"></div>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
@endif
|
||||
@if ($onTheClock !== false && $onTheClock["id"] != $parentTicketId)
|
||||
<span class='working'>
|
||||
@if($style=="full")
|
||||
{{ __("text.timer_set_other_todo") }}
|
||||
@else
|
||||
<span class="fa-solid fa-user-clock" style="font-size:16px; padding-top:3px; color:var(--grey);" data-tippy-content="{{ __("text.timer_set_other_todo") }}"></span>
|
||||
@endif
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
43
app/Domain/Tickets/Templates/partials/timerLink.blade.php
Normal file
43
app/Domain/Tickets/Templates/partials/timerLink.blade.php
Normal file
@@ -0,0 +1,43 @@
|
||||
@props([
|
||||
'parentTicketId' => false,
|
||||
'onTheClock' => false
|
||||
])
|
||||
|
||||
<li id="timerContainer-{{ $parentTicketId }}"
|
||||
hx-get="{{BASE_URL}}/tickets/timerButton/get-status/{{ $parentTicketId }}"
|
||||
hx-trigger="timerUpdate from:body"
|
||||
hx-swap="outerHTML"
|
||||
class="timerContainer">
|
||||
|
||||
@if ($onTheClock === false)
|
||||
<a href="javascript:void(0);" data-value="{{ $parentTicketId }}"
|
||||
hx-patch="{{ BASE_URL }}/hx/timesheets/stopwatch/start-timer/"
|
||||
hx-target="#timerHeadMenu"
|
||||
hx-swap="outerHTML"
|
||||
hx-vals='{"ticketId": "{{ $parentTicketId }}", "action":"start"}'>
|
||||
<span class="fa-regular fa-clock"></span> {{ __("links.start_work") }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if ($onTheClock !== false && $onTheClock["id"] == $parentTicketId)
|
||||
<a href="javascript:void(0);" data-value="{{ $parentTicketId }}"
|
||||
hx-patch="{{ BASE_URL }}/hx/timesheets/stopwatch/stop-timer/"
|
||||
hx-target="#timerHeadMenu"
|
||||
hx-vals='{"ticketId": "{{ $parentTicketId }}", "action":"stop"}'
|
||||
hx-swap="outerHTML">
|
||||
<span class="fa fa-stop"></span>
|
||||
|
||||
@if (is_array($onTheClock) == true)
|
||||
{!! sprintf(__("links.stop_work_started_at"), date(__("language.timeformat"), $onTheClock["since"])) !!}
|
||||
@else
|
||||
{!! sprintf(__("links.stop_work_started_at"), date(__("language.timeformat"), time())) !!}
|
||||
@endif
|
||||
</a>
|
||||
@endif
|
||||
@if ($onTheClock !== false && $onTheClock["id"] != $parentTicketId)
|
||||
<span class='working'>
|
||||
{{ __("text.timer_set_other_todo") }}
|
||||
</span>
|
||||
@endif
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user