mirror of
https://github.com/kevinpapst/kimai2.git
synced 2025-03-17 14:32:38 +00:00
Release 2.0.21 (#4030)
* increase padding in wizard * fix theme chooser in wizard * improve dynamic export columns in PDFs * added label for total column * bump theme bundle * improve avatar list spacing * be more flexible parsing times - fixes #4031
This commit is contained in:
parent
29624354a2
commit
9bd5965ecd
16 changed files with 148 additions and 113 deletions
|
@ -147,18 +147,49 @@ export default class KimaiTimesheetForm extends KimaiFormPlugin {
|
|||
return null;
|
||||
}
|
||||
|
||||
const date = this.getDateUtils().fromFormat(
|
||||
this._beginDate.value + ' ' + this._beginTime.value,
|
||||
this._beginDate.dataset['format'] + ' ' + this._beginTime.dataset['format'],
|
||||
);
|
||||
let date = this._parseBegin(this._beginTime.dataset['format']);
|
||||
|
||||
if (date.invalid) {
|
||||
date = this._parseBegin(this._fixTimeFormat(this._beginTime.dataset['format']));
|
||||
|
||||
if (date.invalid) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
_parseBegin(timeFormat)
|
||||
{
|
||||
return this.getDateUtils().fromFormat(
|
||||
this._beginDate.value + ' ' + this._beginTime.value,
|
||||
this._beginDate.dataset['format'] + ' ' + timeFormat,
|
||||
);
|
||||
}
|
||||
|
||||
_parseEnd(endDate, timeFormat)
|
||||
{
|
||||
let date = this.getDateUtils().fromFormat(
|
||||
endDate.toFormat('yyyy-LL-dd') + ' ' + this._endTime.value,
|
||||
'yyyy-LL-dd ' + timeFormat,
|
||||
);
|
||||
|
||||
if (date.invalid) {
|
||||
date = this.getDateUtils().fromFormat(
|
||||
endDate.toFormat('yyyy-LL-dd') + ' ' + this._endTime.value,
|
||||
'yyyy-LL-dd ' + this._fixTimeFormat(timeFormat),
|
||||
);
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
_fixTimeFormat(format)
|
||||
{
|
||||
return format.replace('HH', 'H').replace('hh', 'h');
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {DateTime|null}
|
||||
* @private
|
||||
|
@ -169,17 +200,11 @@ export default class KimaiTimesheetForm extends KimaiFormPlugin {
|
|||
return null;
|
||||
}
|
||||
|
||||
let date = this.getDateUtils().fromFormat(
|
||||
DateTime.now().toFormat('yyyy-LL-dd') + ' ' + this._endTime.value,
|
||||
'yyyy-LL-dd ' + this._endTime.dataset['format'],
|
||||
);
|
||||
let date = this._parseEnd(DateTime.now(), this._endTime.dataset['format']);
|
||||
|
||||
const begin = this._getBegin();
|
||||
if (begin !== null) {
|
||||
date = this.getDateUtils().fromFormat(
|
||||
begin.toFormat('yyyy-LL-dd') + ' ' + this._endTime.value,
|
||||
'yyyy-LL-dd ' + this._endTime.dataset['format'],
|
||||
);
|
||||
date = this._parseEnd(begin, this._endTime.dataset['format']);
|
||||
|
||||
if (date < begin) {
|
||||
date = date.plus({days: 1});
|
||||
|
|
|
@ -42,5 +42,5 @@ table.dataTable.table > tbody > tr > td {
|
|||
|
||||
/* decreases the margin between avatars, see https://github.com/tabler/tabler/issues/1567 */
|
||||
.avatar-list-stacked .avatar {
|
||||
margin-right: calc(var(--tblr-avatar-size)*-.1)!important;
|
||||
margin-right: calc(var(--tblr-avatar-size)*-.2)!important;
|
||||
}
|
|
@ -34,7 +34,7 @@
|
|||
"friendsofsymfony/rest-bundle": "^3.0",
|
||||
"gedmo/doctrine-extensions": "^3.6",
|
||||
"jms/serializer-bundle": "^5.0",
|
||||
"kevinpapst/tabler-bundle": "^0.17",
|
||||
"kevinpapst/tabler-bundle": "^0.18",
|
||||
"league/csv": "^9.4",
|
||||
"mpdf/mpdf": "^8.0",
|
||||
"nelmio/api-doc-bundle": "^4.0",
|
||||
|
|
14
composer.lock
generated
14
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "63b69c5c8020a78b308210425547d46c",
|
||||
"content-hash": "aedc077bff6e5890b17415527e991e17",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
|
@ -2416,16 +2416,16 @@
|
|||
},
|
||||
{
|
||||
"name": "kevinpapst/tabler-bundle",
|
||||
"version": "0.17",
|
||||
"version": "0.18",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/kevinpapst/TablerBundle.git",
|
||||
"reference": "0061d82a28059cf876cf819f1780607aac131d32"
|
||||
"reference": "f438b55cd393331385700a30515d3966747ecd7a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/kevinpapst/TablerBundle/zipball/0061d82a28059cf876cf819f1780607aac131d32",
|
||||
"reference": "0061d82a28059cf876cf819f1780607aac131d32",
|
||||
"url": "https://api.github.com/repos/kevinpapst/TablerBundle/zipball/f438b55cd393331385700a30515d3966747ecd7a",
|
||||
"reference": "f438b55cd393331385700a30515d3966747ecd7a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2474,7 +2474,7 @@
|
|||
"description": "Admin/Backend theme bundle for Symfony based on Tabler.io",
|
||||
"support": {
|
||||
"issues": "https://github.com/kevinpapst/TablerBundle/issues",
|
||||
"source": "https://github.com/kevinpapst/TablerBundle/tree/0.17"
|
||||
"source": "https://github.com/kevinpapst/TablerBundle/tree/0.18"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -2486,7 +2486,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-05-15T18:25:59+00:00"
|
||||
"time": "2023-05-16T16:30:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laminas/laminas-escaper",
|
||||
|
|
File diff suppressed because one or more lines are too long
2
public/build/app.70a820f6.js
Normal file
2
public/build/app.70a820f6.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -3,10 +3,10 @@
|
|||
"app": {
|
||||
"js": [
|
||||
"/build/runtime.f0079159.js",
|
||||
"/build/app.45d541e0.js"
|
||||
"/build/app.70a820f6.js"
|
||||
],
|
||||
"css": [
|
||||
"/build/app.66162a27.css"
|
||||
"/build/app.f43008a1.css"
|
||||
]
|
||||
},
|
||||
"export-pdf": {
|
||||
|
@ -63,8 +63,8 @@
|
|||
},
|
||||
"integrity": {
|
||||
"/build/runtime.f0079159.js": "sha384-H22sAW1aTvyIPqvHOvGXWSWTxf0y6mptp+MsVmyXCfjx/WJjBbhX9gbUZ+qIuihV",
|
||||
"/build/app.45d541e0.js": "sha384-KhCwP7jLZJ8+g02Lr+oITVZ8woE+EU01t/VQNuDyHmpVPCVggaaoI9EU9lJeB62l",
|
||||
"/build/app.66162a27.css": "sha384-TeR8dC4yDJMVshAZ+sa3WotB4RqTHOiTtHROHg7jXEE29WCabeBlXB6MKiPdnsu3",
|
||||
"/build/app.70a820f6.js": "sha384-WKz8wOwY8EYT1ncPkMRnEE3q5LnP76keOZxTD410dXpsowUktUg+vWUqDhVXwLat",
|
||||
"/build/app.f43008a1.css": "sha384-pZxN+QyK3/ggIPic7tC9sHJImYoGGtVX2CRA2JqH/BK5RYd2UGOsAhb5VXzjLX0l",
|
||||
"/build/export-pdf.d367a32e.js": "sha384-Z5baqnzjI636nYFs4g63ViIKBZKRW4Jhv/7PQmTEQlqhfA7eK0vUMUtiyy0R5A9u",
|
||||
"/build/export-pdf.d8a6c23b.css": "sha384-ztepocHE4rnGE9eKZ4kL6jTKaePUyiwiB9TjJjstjpf/ckcKg1HedrEOOk/8ElJg",
|
||||
"/build/invoice.2604495e.js": "sha384-D6JvhGSqlx7z72b/qD3nF3QDXPy+XsCSRGtWfs1icjDKOcd2UzuXwuSa/E1Fg2TJ",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"build/app.css": "/build/app.66162a27.css",
|
||||
"build/app.js": "/build/app.45d541e0.js",
|
||||
"build/app.css": "/build/app.f43008a1.css",
|
||||
"build/app.js": "/build/app.70a820f6.js",
|
||||
"build/export-pdf.css": "/build/export-pdf.d8a6c23b.css",
|
||||
"build/export-pdf.js": "/build/export-pdf.d367a32e.js",
|
||||
"build/invoice.css": "/build/invoice.5bea118e.css",
|
||||
|
|
|
@ -17,11 +17,11 @@ class Constants
|
|||
/**
|
||||
* The current release version
|
||||
*/
|
||||
public const VERSION = '2.0.20';
|
||||
public const VERSION = '2.0.21';
|
||||
/**
|
||||
* The current release: major * 10000 + minor * 100 + patch
|
||||
*/
|
||||
public const VERSION_ID = 20020;
|
||||
public const VERSION_ID = 20021;
|
||||
/**
|
||||
* The software name
|
||||
*/
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="summary">
|
||||
<td></td>
|
||||
<td>{{ 'sum.total'|trans }}</td>
|
||||
<td class="text-end total">
|
||||
{{ work_times_result(summaries.expectedTime, summaries.actualTime, decimal) }}
|
||||
</td>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
{% set showCustomerSummary = showCustomerSummary ?? true %}
|
||||
{% set showTotalSummary = showTotalSummary ?? true %}
|
||||
{% set showDateTimeShort = showDateTimeShort ?? false %}
|
||||
{% set showSummaryHourlyRate = showSummaryHourlyRate ?? false %}
|
||||
{% set now = create_date('now', app.user) %}
|
||||
{% set decimal = decimal ?? false %}
|
||||
{# this is only triggered, if a user exports from his personal timesheet screen #}
|
||||
|
@ -14,8 +15,26 @@
|
|||
{# if exporting via the admin screen, users without view_rate_own_timesheet might still see their own rates #}
|
||||
{% set showRateColumn = is_granted('view_rate_own_timesheet') %}
|
||||
{% endif %}
|
||||
{% set summaryColumns = ['customer', 'project'] %}
|
||||
{% if showTimeBudget %}
|
||||
{% set summaryColumns = summaryColumns|merge(['timeBudget']) %}
|
||||
{% endif %}
|
||||
{% if showRateBudget %}
|
||||
{% set summaryColumns = summaryColumns|merge(['budget']) %}
|
||||
{% endif %}
|
||||
{% if showSummaryHourlyRate %}
|
||||
{% set summaryColumns = summaryColumns|merge(['hourlyRate']) %}
|
||||
{% endif %}
|
||||
{% set summaryColumns = summaryColumns|merge(['duration']) %}
|
||||
{% if showRateColumn %}
|
||||
{% if showInternalRate %}
|
||||
{% set summaryColumns = summaryColumns|merge(['internalRate']) %}
|
||||
{% endif %}
|
||||
{% set summaryColumns = summaryColumns|merge(['rate']) %}
|
||||
{% endif %}
|
||||
<html{% if app.request is defined and app.request is not null %} lang="{{ app.request.locale }}"{% endif %}>
|
||||
<head>
|
||||
<title>{% block document_title %}{{ 'export'|trans }}{% endblock %}</title>
|
||||
{% block styles %}
|
||||
<style>
|
||||
{{ encore_entry_css_source('export-pdf')|raw }}
|
||||
|
@ -64,21 +83,15 @@ mpdf-->
|
|||
<table class="items">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for summaryColumn in summaryColumns %}
|
||||
{% if summaryColumn == 'customer' %}
|
||||
<th>{{ 'customer'|trans }}</th>
|
||||
{% elseif summaryColumn == 'project' %}
|
||||
<th>{{ 'project'|trans }}</th>
|
||||
{% if showTimeBudget %}
|
||||
<th class="center">{{ 'timeBudget'|trans }}</th>
|
||||
{% endif %}
|
||||
{% if showRateBudget %}
|
||||
<th class="center">{{ 'budget'|trans }}</th>
|
||||
{% endif %}
|
||||
<th class="center">{{ 'duration'|trans }}</th>
|
||||
{% if showRateColumn %}
|
||||
{% if showInternalRate %}
|
||||
<th class="center">{{ 'internalRate'|trans }}</th>
|
||||
{% endif %}
|
||||
<th class="center">{{ 'rate'|trans }}</th>
|
||||
{% else %}
|
||||
<th class="center">{{ summaryColumn|trans }}</th>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -106,21 +119,17 @@ mpdf-->
|
|||
{% endif %}
|
||||
{% if customer is not same as(summary.customer) %}
|
||||
<tr class="summary">
|
||||
<td colspan="2">
|
||||
</td>
|
||||
{% if showTimeBudget %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% if showRateBudget %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% for summaryColumn in summaryColumns %}
|
||||
{% if summaryColumn == 'duration' %}
|
||||
<td class="totals duration">{{ customerDuration|duration(decimal) }}</td>
|
||||
{% if showRateColumn %}
|
||||
{% if showInternalRate %}
|
||||
{% elseif summaryColumn == 'internalRate' %}
|
||||
<td class="totals cost">{{ customerInternalRate|money(customerCurrency) }}</td>
|
||||
{% endif %}
|
||||
{% elseif summaryColumn == 'rate' %}
|
||||
<td class="totals cost">{{ customerRate|money(customerCurrency) }}</td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% set customerCurrency = summary.currency %}
|
||||
{% set customer = summary.customer %}
|
||||
|
@ -130,29 +139,34 @@ mpdf-->
|
|||
{% set customerCount = 0 %}
|
||||
{% endif %}
|
||||
<tr class="{{ cycle(['odd', 'even'], customerCount) }}">
|
||||
{% for summaryColumn in summaryColumns %}
|
||||
{% if summaryColumn == 'customer' %}
|
||||
<td>{{ summary.customer }}</td>
|
||||
{% elseif summaryColumn == 'project' %}
|
||||
<td>{{ summary.project }}</td>
|
||||
{% if showTimeBudget %}
|
||||
{% elseif summaryColumn == 'timeBudget' %}
|
||||
<td class="center">
|
||||
{% if budgets[id] is defined and budgets[id].time_left > 0 %}
|
||||
{{ budgets[id].time_left|duration(decimal) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if showRateBudget %}
|
||||
{% elseif summaryColumn == 'budget' %}
|
||||
<td class="center">
|
||||
{% if budgets[id] is defined and budgets[id].money_left > 0 %}
|
||||
{{ budgets[id].money_left|money(summary.currency) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% elseif summaryColumn == 'hourlyRate' %}
|
||||
{% set summaryHourlyRate = summary.duration/3600 %}
|
||||
<td class="cost">{{ summaryHourlyRate > 0 ? (summary.rate/summaryHourlyRate)|money(summary.currency) : '' }}</td>
|
||||
{% elseif summaryColumn == 'duration' %}
|
||||
<td class="duration">{{ summary.duration|duration(decimal) }}</td>
|
||||
{% if showRateColumn %}
|
||||
{% if showInternalRate %}
|
||||
{% elseif summaryColumn == 'internalRate' %}
|
||||
<td class="cost">{{ summary.rate_internal|money(summary.currency) }}</td>
|
||||
{% endif %}
|
||||
{% elseif summaryColumn == 'rate' %}
|
||||
<td class="cost">{{ summary.rate|money(summary.currency) }}</td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% set customerDuration = customerDuration + summary.duration %}
|
||||
{% set customerRate = customerRate + summary.rate %}
|
||||
|
@ -161,20 +175,17 @@ mpdf-->
|
|||
{% endfor %}
|
||||
{% if showCustomerSummary and customer is not same as(null) %}
|
||||
<tr class="summary">
|
||||
<td colspan="2"></td>
|
||||
{% if showTimeBudget %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% if showRateBudget %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% for summaryColumn in summaryColumns %}
|
||||
{% if summaryColumn == 'duration' %}
|
||||
<td class="totals duration">{{ customerDuration|duration(decimal) }}</td>
|
||||
{% if showRateColumn %}
|
||||
{% if showInternalRate %}
|
||||
{% elseif summaryColumn == 'internalRate' %}
|
||||
<td class="totals cost">{{ customerInternalRate|money(customerCurrency) }}</td>
|
||||
{% endif %}
|
||||
{% elseif summaryColumn == 'rate' %}
|
||||
<td class="totals cost">{{ customerRate|money(customerCurrency) }}</td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if showTotalSummary and not multiCurrency %}
|
||||
|
@ -182,19 +193,17 @@ mpdf-->
|
|||
<td class="totals" colspan="2">
|
||||
{{ 'sum.total'|trans }}
|
||||
</td>
|
||||
{% if showTimeBudget %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% if showRateBudget %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% for summaryColumn in summaryColumns %}
|
||||
{% if summaryColumn == 'duration' %}
|
||||
<td class="totals duration">{{ totalDuration|duration(decimal) }}</td>
|
||||
{% if showRateColumn %}
|
||||
{% if showInternalRate %}
|
||||
{% elseif summaryColumn == 'internalRate' %}
|
||||
<td class="totals cost">{{ totalInternalRate|money(customerCurrency) }}</td>
|
||||
{% endif %}
|
||||
{% elseif summaryColumn == 'rate' %}
|
||||
<td class="totals cost">{{ totalRate|money(customerCurrency) }}</td>
|
||||
{% elseif summaryColumn not in ['customer', 'project'] %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{% from '@Tabler/components/button.html.twig' import button %}
|
||||
|
||||
{% block wizard_content %}
|
||||
<div class="card-body text-center py-4 p-sm-5">
|
||||
<div class="card-body text-center py-4 p-sm-7">
|
||||
<img src="{{ asset('wizard/done.png') }}" height="120" class="mb-n2" alt="Illustration by Katerina Limpitsouni from https://undraw.co/">
|
||||
<h1 class="mt-5">{{ 'wizard.done.title'|trans({}, 'wizard') }}</h1>
|
||||
<p class="text-body-secondary">{{ 'wizard.done.description'|trans({}, 'wizard') }}</p>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends 'wizard/layout.html.twig' %}
|
||||
|
||||
{% block wizard_content %}
|
||||
<div class="card-body text-center py-4 p-sm-5">
|
||||
<div class="card-body text-center py-4 p-sm-7">
|
||||
<img src="{{ asset('wizard/time-management.png') }}" height="120" class="mb-n2" alt="Illustration by Katerina Limpitsouni from https://undraw.co/">
|
||||
<h1 class="mt-5">{{ 'wizard.intro.title'|trans({}, 'wizard') }}</h1>
|
||||
<p class="text-body-secondary">{{ 'wizard.intro.description'|trans({}, 'wizard') }}</p>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends 'wizard/layout.html.twig' %}
|
||||
|
||||
{% block wizard_content %}
|
||||
<div class="card-body text-center py-4 p-sm-5">
|
||||
<div class="card-body text-center py-4 p-sm-6">
|
||||
<h1 class="mt-2">{{ 'wizard.profile.title'|trans({}, 'wizard') }}</h1>
|
||||
<p class="text-body-secondary">{{ 'wizard.profile.description'|trans({}, 'wizard') }}</p>
|
||||
</div>
|
||||
|
@ -19,10 +19,11 @@
|
|||
const skinChooser = document.getElementById('form_skin');
|
||||
if (skinChooser !== null) {
|
||||
skinChooser.addEventListener('change', () => {
|
||||
document.body.classList.remove('theme-light');
|
||||
document.body.classList.remove('theme-default');
|
||||
document.body.classList.remove('theme-dark');
|
||||
document.body.classList.add('theme-' + skinChooser.value);
|
||||
document.querySelectorAll('[data-bs-theme]').forEach(
|
||||
element => {
|
||||
element.dataset['bsTheme'] = (skinChooser.value === 'default' ? 'light' : 'dark');
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue