Release 2.44 (#5699)

This commit is contained in:
Kevin Papst
2025-11-19 14:28:54 +01:00
committed by GitHub
parent 8a42c9640f
commit a15c1e56cb
33 changed files with 144 additions and 157 deletions

View File

@@ -6,7 +6,7 @@
*/ */
/*! /*!
* [KIMAI] KimaiFormPlugin: base class for all none ID plugin that handle forms * [KIMAI] KimaiFormTomselectPlugin: base form plugin for everyone using tomselect
*/ */
import KimaiFormPlugin from './KimaiFormPlugin'; import KimaiFormPlugin from './KimaiFormPlugin';

View File

@@ -12,6 +12,11 @@
import KimaiPlugin from '../KimaiPlugin'; import KimaiPlugin from '../KimaiPlugin';
import { Modal } from 'bootstrap'; import { Modal } from 'bootstrap';
/**
* Use like this:
* <a href="{{ path('your-route') }}" class="remote-modal-load" data-modal-id="remote_modal" data-modal-class="p-0" data-modal-title="Some title" title="Some title">Modal</a>
* <a href="{{ path('your-route') }}" class="remote-modal-load" data-modal-title="Some title" title="Some title">Modal</a>
*/
export default class KimaiRemoteModal extends KimaiPlugin { export default class KimaiRemoteModal extends KimaiPlugin {
constructor() constructor()
@@ -39,38 +44,6 @@ export default class KimaiRemoteModal extends KimaiPlugin {
for (let link of document.querySelectorAll(this._selector)) { for (let link of document.querySelectorAll(this._selector)) {
link.addEventListener('click', this.handle); link.addEventListener('click', this.handle);
} }
document.addEventListener('kimai.closeRemoteModal', () => { this._hide(); });
}
/**
* @param {HTMLElement} element
* @private
*/
_initElement(element)
{
for (let link of element.querySelectorAll('a.remote-modal-reload')) {
link.addEventListener('click', this.handle);
}
}
_hide()
{
this._getModal().hide();
}
_getModalElement()
{
return document.getElementById('remote_modal');
}
/**
* @returns {Modal}
* @private
*/
_getModal()
{
return Modal.getOrCreateInstance(this._getModalElement());
} }
/** /**
@@ -85,21 +58,33 @@ export default class KimaiRemoteModal extends KimaiPlugin {
return; return;
} }
let modalSelector = 'remote_modal';
if (element.dataset['modalId'] !== undefined) {
modalSelector = element.dataset['modalId'];
}
const modalElement = document.getElementById(modalSelector);
if (modalElement === null) {
console.log('Could not find modal with ID: ' + modalSelector);
}
return response.text().then(html => { return response.text().then(html => {
const newFormHtml = document.createElement('div'); const modalBody = document.createElement('div');
newFormHtml.classList.add('modal-body'); modalBody.classList.add('modal-body');
newFormHtml.classList.add('p-0'); if (element.dataset['modalClass'] !== undefined) {
newFormHtml.innerHTML = html; modalBody.classList.add(element.dataset['modalClass']);
}
modalBody.innerHTML = html;
this._initElement(newFormHtml); for (let link of modalBody.querySelectorAll('a.remote-modal-reload')) {
link.addEventListener('click', this.handle);
const modal = this._getModalElement();
modal.querySelector('.modal-body').replaceWith(newFormHtml);
if (element.dataset['modalTitle'] !== undefined) {
modal.querySelector('.modal-title').textContent = element.dataset['modalTitle'];
} }
this._getModal().show(); modalElement.querySelector('.modal-body').replaceWith(modalBody);
if (element.dataset['modalTitle'] !== undefined) {
modalElement.querySelector('.modal-title').textContent = element.dataset['modalTitle'];
}
Modal.getOrCreateInstance(modalElement).show();
}); });
}) })
.catch((reason) => { .catch((reason) => {

95
composer.lock generated
View File

@@ -896,16 +896,16 @@
}, },
{ {
"name": "doctrine/doctrine-migrations-bundle", "name": "doctrine/doctrine-migrations-bundle",
"version": "3.6.0", "version": "3.7.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/DoctrineMigrationsBundle.git", "url": "https://github.com/doctrine/DoctrineMigrationsBundle.git",
"reference": "49ecc564568d7da101779112579e78b677fbc94a" "reference": "1e380c6dd8ac8488217f39cff6b77e367f1a644b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/49ecc564568d7da101779112579e78b677fbc94a", "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/1e380c6dd8ac8488217f39cff6b77e367f1a644b",
"reference": "49ecc564568d7da101779112579e78b677fbc94a", "reference": "1e380c6dd8ac8488217f39cff6b77e367f1a644b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -961,7 +961,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/doctrine/DoctrineMigrationsBundle/issues", "issues": "https://github.com/doctrine/DoctrineMigrationsBundle/issues",
"source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.6.0" "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.7.0"
}, },
"funding": [ "funding": [
{ {
@@ -977,7 +977,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-11-07T19:40:03+00:00" "time": "2025-11-15T19:02:59+00:00"
}, },
{ {
"name": "doctrine/event-manager", "name": "doctrine/event-manager",
@@ -2330,16 +2330,16 @@
}, },
{ {
"name": "horstoeko/zugferd", "name": "horstoeko/zugferd",
"version": "v1.0.116", "version": "v1.0.117",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/horstoeko/zugferd.git", "url": "https://github.com/horstoeko/zugferd.git",
"reference": "23fe5478a394ed96e15aaaff7451742f33d36f02" "reference": "7c2fdb58e0910e199b1fd2162ae5d33a1e62e933"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/horstoeko/zugferd/zipball/23fe5478a394ed96e15aaaff7451742f33d36f02", "url": "https://api.github.com/repos/horstoeko/zugferd/zipball/7c2fdb58e0910e199b1fd2162ae5d33a1e62e933",
"reference": "23fe5478a394ed96e15aaaff7451742f33d36f02", "reference": "7c2fdb58e0910e199b1fd2162ae5d33a1e62e933",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2398,9 +2398,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/horstoeko/zugferd/issues", "issues": "https://github.com/horstoeko/zugferd/issues",
"source": "https://github.com/horstoeko/zugferd/tree/v1.0.116" "source": "https://github.com/horstoeko/zugferd/tree/v1.0.117"
}, },
"time": "2025-10-14T12:07:57+00:00" "time": "2025-11-17T14:09:15+00:00"
}, },
{ {
"name": "horstoeko/zugferdublbridge", "name": "horstoeko/zugferdublbridge",
@@ -6855,16 +6855,16 @@
}, },
{ {
"name": "symfony/flex", "name": "symfony/flex",
"version": "v2.9.0", "version": "v2.10.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/flex.git", "url": "https://github.com/symfony/flex.git",
"reference": "94b37978c9982dc41c5b6a4147892d2d3d1b9ce6" "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/flex/zipball/94b37978c9982dc41c5b6a4147892d2d3d1b9ce6", "url": "https://api.github.com/repos/symfony/flex/zipball/9cd384775973eabbf6e8b05784dda279fc67c28d",
"reference": "94b37978c9982dc41c5b6a4147892d2d3d1b9ce6", "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -6872,7 +6872,8 @@
"php": ">=8.1" "php": ">=8.1"
}, },
"conflict": { "conflict": {
"composer/semver": "<1.7.2" "composer/semver": "<1.7.2",
"symfony/dotenv": "<5.4"
}, },
"require-dev": { "require-dev": {
"composer/composer": "^2.1", "composer/composer": "^2.1",
@@ -6903,7 +6904,7 @@
"description": "Composer plugin for Symfony", "description": "Composer plugin for Symfony",
"support": { "support": {
"issues": "https://github.com/symfony/flex/issues", "issues": "https://github.com/symfony/flex/issues",
"source": "https://github.com/symfony/flex/tree/v2.9.0" "source": "https://github.com/symfony/flex/tree/v2.10.0"
}, },
"funding": [ "funding": [
{ {
@@ -6923,7 +6924,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-10-31T15:22:50+00:00" "time": "2025-11-16T09:38:19+00:00"
}, },
{ {
"name": "symfony/form", "name": "symfony/form",
@@ -10410,22 +10411,22 @@
}, },
{ {
"name": "twig/extra-bundle", "name": "twig/extra-bundle",
"version": "v3.22.0", "version": "v3.22.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/twigphp/twig-extra-bundle.git", "url": "https://github.com/twigphp/twig-extra-bundle.git",
"reference": "6d253f0fe28a83a045497c8fb3ea9bfe84e82cf4" "reference": "b6534bc925bec930004facca92fccebd0c809247"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/6d253f0fe28a83a045497c8fb3ea9bfe84e82cf4", "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/b6534bc925bec930004facca92fccebd0c809247",
"reference": "6d253f0fe28a83a045497c8fb3ea9bfe84e82cf4", "reference": "b6534bc925bec930004facca92fccebd0c809247",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.1.0", "php": ">=8.1.0",
"symfony/framework-bundle": "^5.4|^6.4|^7.0", "symfony/framework-bundle": "^5.4|^6.4|^7.0|^8.0",
"symfony/twig-bundle": "^5.4|^6.4|^7.0", "symfony/twig-bundle": "^5.4|^6.4|^7.0|^8.0",
"twig/twig": "^3.2|^4.0" "twig/twig": "^3.2|^4.0"
}, },
"require-dev": { "require-dev": {
@@ -10468,7 +10469,7 @@
"twig" "twig"
], ],
"support": { "support": {
"source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.22.0" "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.22.1"
}, },
"funding": [ "funding": [
{ {
@@ -10480,7 +10481,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-09-15T05:57:37+00:00" "time": "2025-11-02T11:00:49+00:00"
}, },
{ {
"name": "twig/inky-extra", "name": "twig/inky-extra",
@@ -10554,21 +10555,21 @@
}, },
{ {
"name": "twig/intl-extra", "name": "twig/intl-extra",
"version": "v3.22.0", "version": "v3.22.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/twigphp/intl-extra.git", "url": "https://github.com/twigphp/intl-extra.git",
"reference": "7393fc911c7315db18a805d3a541ac7bb9e4fdc0" "reference": "93ac31e53cdd3f2e541f42690cd0c54ca8138ab1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/twigphp/intl-extra/zipball/7393fc911c7315db18a805d3a541ac7bb9e4fdc0", "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/93ac31e53cdd3f2e541f42690cd0c54ca8138ab1",
"reference": "7393fc911c7315db18a805d3a541ac7bb9e4fdc0", "reference": "93ac31e53cdd3f2e541f42690cd0c54ca8138ab1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.1.0", "php": ">=8.1.0",
"symfony/intl": "^5.4|^6.4|^7.0", "symfony/intl": "^5.4|^6.4|^7.0|^8.0",
"twig/twig": "^3.13|^4.0" "twig/twig": "^3.13|^4.0"
}, },
"require-dev": { "require-dev": {
@@ -10602,7 +10603,7 @@
"twig" "twig"
], ],
"support": { "support": {
"source": "https://github.com/twigphp/intl-extra/tree/v3.22.0" "source": "https://github.com/twigphp/intl-extra/tree/v3.22.1"
}, },
"funding": [ "funding": [
{ {
@@ -10614,25 +10615,25 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-09-15T06:05:04+00:00" "time": "2025-11-02T11:00:49+00:00"
}, },
{ {
"name": "twig/string-extra", "name": "twig/string-extra",
"version": "v3.22.0", "version": "v3.22.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/twigphp/string-extra.git", "url": "https://github.com/twigphp/string-extra.git",
"reference": "4b3337544ac8f76c280def94e32b53acfaec0589" "reference": "d5f16e0bec548bc96cce255b5f43d90492b8ce13"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/twigphp/string-extra/zipball/4b3337544ac8f76c280def94e32b53acfaec0589", "url": "https://api.github.com/repos/twigphp/string-extra/zipball/d5f16e0bec548bc96cce255b5f43d90492b8ce13",
"reference": "4b3337544ac8f76c280def94e32b53acfaec0589", "reference": "d5f16e0bec548bc96cce255b5f43d90492b8ce13",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.1.0", "php": ">=8.1.0",
"symfony/string": "^5.4|^6.4|^7.0", "symfony/string": "^5.4|^6.4|^7.0|^8.0",
"symfony/translation-contracts": "^1.1|^2|^3", "symfony/translation-contracts": "^1.1|^2|^3",
"twig/twig": "^3.13|^4.0" "twig/twig": "^3.13|^4.0"
}, },
@@ -10669,7 +10670,7 @@
"unicode" "unicode"
], ],
"support": { "support": {
"source": "https://github.com/twigphp/string-extra/tree/v3.22.0" "source": "https://github.com/twigphp/string-extra/tree/v3.22.1"
}, },
"funding": [ "funding": [
{ {
@@ -10681,7 +10682,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-01-31T20:45:36+00:00" "time": "2025-11-02T11:00:49+00:00"
}, },
{ {
"name": "twig/twig", "name": "twig/twig",
@@ -10921,16 +10922,16 @@
}, },
{ {
"name": "zircote/swagger-php", "name": "zircote/swagger-php",
"version": "5.7.0", "version": "5.7.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/zircote/swagger-php.git", "url": "https://github.com/zircote/swagger-php.git",
"reference": "39fcd46e79c2f3cfbf56cf5a92a86108c8eed401" "reference": "374262ed61670c5bd5f050cd9e4dc850d0a092ef"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/39fcd46e79c2f3cfbf56cf5a92a86108c8eed401", "url": "https://api.github.com/repos/zircote/swagger-php/zipball/374262ed61670c5bd5f050cd9e4dc850d0a092ef",
"reference": "39fcd46e79c2f3cfbf56cf5a92a86108c8eed401", "reference": "374262ed61670c5bd5f050cd9e4dc850d0a092ef",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -11003,9 +11004,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/zircote/swagger-php/issues", "issues": "https://github.com/zircote/swagger-php/issues",
"source": "https://github.com/zircote/swagger-php/tree/5.7.0" "source": "https://github.com/zircote/swagger-php/tree/5.7.1"
}, },
"time": "2025-11-11T03:41:35+00:00" "time": "2025-11-16T20:00:36+00:00"
} }
], ],
"packages-dev": [ "packages-dev": [

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -132,10 +132,6 @@
* [KIMAI] KimaiPlugin: base class for all plugins * [KIMAI] KimaiPlugin: base class for all plugins
*/ */
/*!
* [KIMAI] KimaiRecentActivities: responsible to reload the users recent activities
*/
/*! /*!
* [KIMAI] KimaiReducedClickHandler: abstract class * [KIMAI] KimaiReducedClickHandler: abstract class
*/ */
@@ -144,6 +140,10 @@
* [KIMAI] KimaiReloadPageWidget: a simple helper to reload the page on events * [KIMAI] KimaiReloadPageWidget: a simple helper to reload the page on events
*/ */
/*!
* [KIMAI] KimaiRemoteModal: load remote content (without forms) into a modal
*/
/*! /*!
* [KIMAI] KimaiStorage: simple wrapper to handle localStorage access * [KIMAI] KimaiStorage: simple wrapper to handle localStorage access
*/ */

View File

@@ -3,7 +3,7 @@
"app": { "app": {
"js": [ "js": [
"/build/runtime.6c399d29.js", "/build/runtime.6c399d29.js",
"/build/app.dd0a8e58.js" "/build/app.e10c6f94.js"
], ],
"css": [ "css": [
"/build/app.8b6629a6.css" "/build/app.8b6629a6.css"
@@ -81,7 +81,7 @@
}, },
"integrity": { "integrity": {
"/build/runtime.6c399d29.js": "sha384-/rm616f12czi8l/27GvWXtb3g608vJZf2XTUKxqCRI4tsa2vUHP+BW90edTok5zC", "/build/runtime.6c399d29.js": "sha384-/rm616f12czi8l/27GvWXtb3g608vJZf2XTUKxqCRI4tsa2vUHP+BW90edTok5zC",
"/build/app.dd0a8e58.js": "sha384-k0DlCny0XdsJHHuyL8B5MNlZTl+s8ZvBFvjwD9xrPQhbZ7ryiTx72pHOi+vaEzB8", "/build/app.e10c6f94.js": "sha384-liXpnmoTHMmDRcTM00CGPemKYTMMCVhSUN0nf3xTFq3Je3EPb5xGrIqGmZSryZbH",
"/build/app.8b6629a6.css": "sha384-mgEEi5bDaaMfrI+/iEBBD2S2sQvjdRJhsYN89QlhqPeUhM39pcxiGx9WVVP31jls", "/build/app.8b6629a6.css": "sha384-mgEEi5bDaaMfrI+/iEBBD2S2sQvjdRJhsYN89QlhqPeUhM39pcxiGx9WVVP31jls",
"/build/app-rtl.7a875ca7.js": "sha384-T7gLI61h9dGeMgzo63vKu4GiDOeLPct9zSUHrceNbhSwIdUmSSNoZ1+d7fKhJJ4/", "/build/app-rtl.7a875ca7.js": "sha384-T7gLI61h9dGeMgzo63vKu4GiDOeLPct9zSUHrceNbhSwIdUmSSNoZ1+d7fKhJJ4/",
"/build/app-rtl.bf451276.css": "sha384-hWaFBRhgNGAmCoe30xfzWFG1qkmKSAXBjZj/GRXY61Gymsq3p2d9sbyC7M6EwumB", "/build/app-rtl.bf451276.css": "sha384-hWaFBRhgNGAmCoe30xfzWFG1qkmKSAXBjZj/GRXY61Gymsq3p2d9sbyC7M6EwumB",

View File

@@ -1,6 +1,6 @@
{ {
"build/app.css": "/build/app.8b6629a6.css", "build/app.css": "/build/app.8b6629a6.css",
"build/app.js": "/build/app.dd0a8e58.js", "build/app.js": "/build/app.e10c6f94.js",
"build/app-rtl.css": "/build/app-rtl.bf451276.css", "build/app-rtl.css": "/build/app-rtl.bf451276.css",
"build/app-rtl.js": "/build/app-rtl.7a875ca7.js", "build/app-rtl.js": "/build/app-rtl.7a875ca7.js",
"build/export-pdf.css": "/build/export-pdf.d8a6c23b.css", "build/export-pdf.css": "/build/export-pdf.d8a6c23b.css",

View File

@@ -17,11 +17,11 @@ final class Constants
/** /**
* The current release version * The current release version
*/ */
public const VERSION = '2.43.0'; public const VERSION = '2.44.0';
/** /**
* The current release: major * 10000 + minor * 100 + patch * The current release: major * 10000 + minor * 100 + patch
*/ */
public const VERSION_ID = 24300; public const VERSION_ID = 24400;
/** /**
* The software name * The software name
*/ */

View File

@@ -471,7 +471,7 @@ final class SystemConfigurationController extends AbstractController
->setType(CustomerTypePatternType::class), ->setType(CustomerTypePatternType::class),
(new Configuration('customer.number_format')) (new Configuration('customer.number_format'))
->setLabel('customer.number_format') ->setLabel('customer.number_format')
->setOptions(['help' => 'allowed_replacer', 'help_translation_parameters' => ['%replacer%' => '{cc}']]) ->setOptions(['help' => 'allowed_replacer', 'help_translation_parameters' => ['%replacer%' => '{cc}, {Y}, {y}, {M}, {m}, {D}, {d}, {YY}, {yy}, {MM}, {DD}']])
->setRequired(true) ->setRequired(true)
->setType(TextType::class) ->setType(TextType::class)
->setTranslationDomain('system-configuration'), ->setTranslationDomain('system-configuration'),
@@ -491,7 +491,7 @@ final class SystemConfigurationController extends AbstractController
->setTranslationDomain('system-configuration'), ->setTranslationDomain('system-configuration'),
(new Configuration('project.number_format')) (new Configuration('project.number_format'))
->setLabel('project.number_format') ->setLabel('project.number_format')
->setOptions(['help' => 'allowed_replacer', 'help_translation_parameters' => ['%replacer%' => '{pc}']]) ->setOptions(['help' => 'allowed_replacer', 'help_translation_parameters' => ['%replacer%' => '{pc}, {Y}, {y}, {M}, {m}, {D}, {d}, {YY}, {yy}, {MM}, {DD}']])
->setRequired(false) ->setRequired(false)
->setType(TextType::class) ->setType(TextType::class)
->setTranslationDomain('system-configuration'), ->setTranslationDomain('system-configuration'),
@@ -507,7 +507,7 @@ final class SystemConfigurationController extends AbstractController
->setType(ActivityTypePatternType::class), ->setType(ActivityTypePatternType::class),
(new Configuration('activity.number_format')) (new Configuration('activity.number_format'))
->setLabel('activity.number_format') ->setLabel('activity.number_format')
->setOptions(['help' => 'allowed_replacer', 'help_translation_parameters' => ['%replacer%' => '{ac}']]) ->setOptions(['help' => 'allowed_replacer', 'help_translation_parameters' => ['%replacer%' => '{ac}, {Y}, {y}, {M}, {m}, {D}, {d}, {YY}, {yy}, {MM}, {DD}']])
->setRequired(false) ->setRequired(false)
->setType(TextType::class) ->setType(TextType::class)
->setTranslationDomain('system-configuration'), ->setTranslationDomain('system-configuration'),

View File

@@ -84,7 +84,7 @@ abstract class TimesheetAbstractController extends AbstractController
} }
if ($this->configuration->isBreakTimeEnabled()) { if ($this->configuration->isBreakTimeEnabled()) {
$table->addColumn('break', ['class' => 'text-end text-nowrap']); $table->addColumn('break', ['class' => 'text-end text-nowrap', 'orderBy' => false]);
} }
$table->addColumn('duration', ['class' => 'text-end text-nowrap']); $table->addColumn('duration', ['class' => 'text-end text-nowrap']);

View File

@@ -19,6 +19,9 @@ class ThemeEvent extends Event
public const HTML_HEAD = 'app.theme.html_head'; public const HTML_HEAD = 'app.theme.html_head';
public const CONTENT_BEFORE = 'app.theme.content_before'; public const CONTENT_BEFORE = 'app.theme.content_before';
public const CONTENT_START = 'app.theme.content_start'; public const CONTENT_START = 'app.theme.content_start';
/**
* The toolbar is rendered twice for small and large screens.
*/
public const TOOLBAR = 'app.theme.toolbar'; public const TOOLBAR = 'app.theme.toolbar';
public const CONTENT_END = 'app.theme.content_end'; public const CONTENT_END = 'app.theme.content_end';
public const CONTENT_AFTER = 'app.theme.content_after'; public const CONTENT_AFTER = 'app.theme.content_after';

View File

@@ -73,12 +73,12 @@ final class RoundingService
public function roundBegin(Timesheet $record): void public function roundBegin(Timesheet $record): void
{ {
foreach ($this->getRoundingRules() as $rounding) { if ($record->getBegin() === null) {
if ($record->getBegin() === null) { return;
continue; }
} $weekday = $record->getBegin()->format('l');
$weekday = $record->getBegin()->format('l');
foreach ($this->getRoundingRules() as $rounding) {
if (\in_array(strtolower($weekday), $rounding['days'], true)) { if (\in_array(strtolower($weekday), $rounding['days'], true)) {
$rounder = $this->getRoundingMode($rounding['mode']); $rounder = $this->getRoundingMode($rounding['mode']);
$rounder->roundBegin($record, $rounding['begin']); $rounder->roundBegin($record, $rounding['begin']);
@@ -88,12 +88,12 @@ final class RoundingService
public function roundEnd(Timesheet $record): void public function roundEnd(Timesheet $record): void
{ {
foreach ($this->getRoundingRules() as $rounding) { if ($record->getEnd() === null) {
if ($record->getEnd() === null) { return;
continue; }
} $weekday = $record->getEnd()->format('l');
$weekday = $record->getEnd()->format('l');
foreach ($this->getRoundingRules() as $rounding) {
if (\in_array(strtolower($weekday), $rounding['days'], true)) { if (\in_array(strtolower($weekday), $rounding['days'], true)) {
$rounder = $this->getRoundingMode($rounding['mode']); $rounder = $this->getRoundingMode($rounding['mode']);
$rounder->roundEnd($record, $rounding['end']); $rounder->roundEnd($record, $rounding['end']);
@@ -103,12 +103,12 @@ final class RoundingService
public function roundDuration(Timesheet $record): void public function roundDuration(Timesheet $record): void
{ {
foreach ($this->getRoundingRules() as $rounding) { if ($record->getEnd() === null) {
if ($record->getEnd() === null) { return;
continue; }
} $weekday = $record->getEnd()->format('l');
$weekday = $record->getEnd()->format('l');
foreach ($this->getRoundingRules() as $rounding) {
if (\in_array(strtolower($weekday), $rounding['days'], true)) { if (\in_array(strtolower($weekday), $rounding['days'], true)) {
$rounder = $this->getRoundingMode($rounding['mode']); $rounder = $this->getRoundingMode($rounding['mode']);
$rounder->roundDuration($record, $rounding['duration']); $rounder->roundDuration($record, $rounding['duration']);
@@ -118,16 +118,12 @@ final class RoundingService
public function applyRoundings(Timesheet $record): void public function applyRoundings(Timesheet $record): void
{ {
if (null === $record->getEnd()) { if ($record->getEnd() === null) {
return; return;
} }
$weekday = $record->getEnd()->format('l');
foreach ($this->getRoundingRules() as $rounding) { foreach ($this->getRoundingRules() as $rounding) {
if ($record->getEnd() === null) {
continue;
}
$weekday = $record->getEnd()->format('l');
if (\in_array(strtolower($weekday), $rounding['days'], true)) { if (\in_array(strtolower($weekday), $rounding['days'], true)) {
$rounder = $this->getRoundingMode($rounding['mode']); $rounder = $this->getRoundingMode($rounding['mode']);
$rounder->roundBegin($record, $rounding['begin']); $rounder->roundBegin($record, $rounding['begin']);

View File

@@ -3,7 +3,7 @@
{% block status %} {% block status %}
{% from "macros/status.html.twig" import status %} {% from "macros/status.html.twig" import status %}
{{ status(constant('App\\Constants::VERSION')) }} {{ status(constant('App\\Constants::VERSION')) }}
{{ status(environment, environment == 'dev' ? 'red' : 'green') }} {{ status(environment, environment == 'dev' ? 'danger' : 'success') }}
{% endblock %} {% endblock %}
{% block main %} {% block main %}

View File

@@ -15,8 +15,8 @@
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col text-truncate" > <div class="col text-truncate" >
<a class="api-link text-decoration-none text-body d-block" href="{{ path('restart_timesheet', {'id': entry.id}) }}" <a class="api-link text-decoration-none text-body d-block" href="{{ path('restart_timesheet', {'id': entry.id}) }}"
data-event="kimai.timesheetStart kimai.timesheetUpdate kimai.closeRemoteModal" data-method="PATCH" data-msg-error="timesheet.start.error" data-event="kimai.timesheetStart kimai.timesheetUpdate" data-method="PATCH" data-msg-error="timesheet.start.error"
data-msg-success="timesheet.start.success"> data-msg-success="timesheet.start.success" data-bs-dismiss="modal">
{{ label_activity(entry.activity) }} {{ label_activity(entry.activity) }}
<div class="d-block text-truncate mt-n1"> <div class="d-block text-truncate mt-n1">
{{ label_project(entry.project) }} {{ label_project(entry.project) }}

View File

@@ -1,18 +1,16 @@
{% macro status(title, color) %} {% macro status(title, color) %}
{% from '@theme/components/status.html.twig' import status %} {% from '@theme/components/status.html.twig' import status %}
{% set options = {} %} {{ status(title, {'color': color|default('primary')}) }}
{% if color %}{% set options = options|merge({'color': color}) %}{% endif %}
{{ status(title, options) }}
{% endmacro %} {% endmacro %}
{% macro status_duration(duration) %} {% macro status_duration(duration) %}
{{ _self.status(duration, 'azure') }} {{ _self.status(duration) }}
{% endmacro %} {% endmacro %}
{% macro status_money(money) %} {% macro status_money(money) %}
{{ _self.status(money, 'blue') }} {{ _self.status(money) }}
{% endmacro %} {% endmacro %}
{% macro status_count(counter) %} {% macro status_count(counter) %}
{{ _self.status(counter, 'teal') }} {{ _self.status(counter) }}
{% endmacro %} {% endmacro %}

View File

@@ -226,10 +226,14 @@
{% endmacro %} {% endmacro %}
{% macro badge_team_access(teams) %} {% macro badge_team_access(teams) %}
{% if teams|length > 0 %} {{ _self.badge_counted(teams|length) }}
{{ _self.badge_counter(teams|length) }} {% endmacro %}
{% macro badge_counted(amount) %}
{% if amount > 0 %}
{{ _self.badge_counter(amount, null, 'primary') }}
{% else %} {% else %}
{{ _self.badge_counter(0, null, 'gray') }} {{ _self.badge_counter(0, null, 'secondary') }}
{% endif %} {% endif %}
{% endmacro %} {% endmacro %}

View File

@@ -1,6 +1,6 @@
{% if is_granted('start_own_timesheet') %} {% if is_granted('start_own_timesheet') %}
<div class="nav-item d-flex me-1 recent-activities"> <div class="nav-item d-flex me-1 recent-activities">
<a href="{{ path('favorites_timesheets') }}" class="btn btn-icon px-0 remote-modal-load recent-activities-btn" data-modal-title="{{ 'recent.activities'|trans }}" tabindex="-1" title="{{ 'recent.activities'|trans }}"> <a href="{{ path('favorites_timesheets') }}" class="btn btn-icon px-0 remote-modal-load recent-activities-btn" data-modal-class="p-0" data-modal-title="{{ 'recent.activities'|trans }}" tabindex="-1" title="{{ 'recent.activities'|trans }}">
{{ icon('repeat', true) }} {{ icon('repeat', true) }}
</a> </a>
</div> </div>

View File

@@ -10,7 +10,7 @@
{% if column == 'name' %} {% if column == 'name' %}
{{ widgets.label_name(entry.name, entry.color|colorize(entry.name)) }} {{ widgets.label_name(entry.name, entry.color|colorize(entry.name)) }}
{% elseif column == 'amount' %} {% elseif column == 'amount' %}
{{ widgets.badge_counter(entry.amount, null, (entry.amount == 0 ? 'yellow' : '')) }} {{ widgets.badge_counted(entry.amount) }}
{% elseif column == 'visible' %} {% elseif column == 'visible' %}
{{ widgets.label_visible(entry.visible) }} {{ widgets.label_visible(entry.visible) }}
{% elseif column == 'actions' %} {% elseif column == 'actions' %}

View File

@@ -16,7 +16,7 @@
{{ widgets.user_avatar(user, tooltip, 'avatar-rounded', (member.teamlead ? 'info' : null)) }} {{ widgets.user_avatar(user, tooltip, 'avatar-rounded', (member.teamlead ? 'info' : null)) }}
{% endfor %} {% endfor %}
{% elseif column == 'amount' %} {% elseif column == 'amount' %}
{{ widgets.badge_counter(entry.members|length) }} {{ widgets.badge_counted(entry.members|length) }}
{% elseif column == 'actions' %} {% elseif column == 'actions' %}
{% set event = actions(app.user, 'team', 'index', {'team': entry}) %} {% set event = actions(app.user, 'team', 'index', {'team': entry}) %}
{{ widgets.table_actions(event.actions) }} {{ widgets.table_actions(event.actions) }}

View File

@@ -77,7 +77,7 @@
{% if lastEntry is not null and entry.end is not null and entry.user is same as (lastEntry.user) %} {% if lastEntry is not null and entry.end is not null and entry.user is same as (lastEntry.user) %}
{% if checkOverlappingDesc and entry.end.timestamp > lastEntry.begin.timestamp %} {% if checkOverlappingDesc and entry.end.timestamp > lastEntry.begin.timestamp %}
{% set class = class ~ ' overlapping' %} {% set class = class ~ ' overlapping' %}
{% elseif checkOverlappingAsc and entry.begin.timestamp < lastEntry.end.timestamp %} {% elseif checkOverlappingAsc and lastEntry.end is not null and entry.begin.timestamp < lastEntry.end.timestamp %}
{% set class = class ~ ' overlapping' %} {% set class = class ~ ' overlapping' %}
{% endif %} {% endif %}
{% endif %} {% endif %}

View File

@@ -39,6 +39,6 @@ class FavoriteControllerTest extends AbstractControllerBaseTestCase
$content = $client->getResponse()->getContent(); $content = $client->getResponse()->getContent();
self::assertNotFalse($content); self::assertNotFalse($content);
self::assertStringContainsString('<a class="api-link text-decoration-none text-body d-block" href="/api/timesheets/', $content); self::assertStringContainsString('<a class="api-link text-decoration-none text-body d-block" href="/api/timesheets/', $content);
self::assertStringContainsString('data-event="kimai.timesheetStart kimai.timesheetUpdate kimai.closeRemoteModal" data-method="PATCH" data-msg-error="timesheet', $content); self::assertStringContainsString('data-event="kimai.timesheetStart kimai.timesheetUpdate" data-method="PATCH" data-msg-error="timesheet', $content);
} }
} }

View File

@@ -2042,7 +2042,7 @@
<source>payment_account_name</source> <source>payment_account_name</source>
<target>IBAN oder Kartennummer</target> <target>IBAN oder Kartennummer</target>
</trans-unit> </trans-unit>
<trans-unit id="jcT9sdfOg6" resname="electronic_invoice"> <trans-unit id="N_ZNih_" resname="electronic_invoice">
<source>electronic_invoice</source> <source>electronic_invoice</source>
<target>E-Rechnung</target> <target>E-Rechnung</target>
</trans-unit> </trans-unit>

View File

@@ -2042,7 +2042,7 @@
<source>payment_account_name</source> <source>payment_account_name</source>
<target>IBAN or card number</target> <target>IBAN or card number</target>
</trans-unit> </trans-unit>
<trans-unit id="jcT9sdfOg6" resname="electronic_invoice"> <trans-unit id="N_ZNih_" resname="electronic_invoice">
<source>electronic_invoice</source> <source>electronic_invoice</source>
<target>E-Invoice</target> <target>E-Invoice</target>
</trans-unit> </trans-unit>

View File

@@ -1986,7 +1986,7 @@
<source>remaining_budget</source> <source>remaining_budget</source>
<target state="translated">Saldo</target> <target state="translated">Saldo</target>
</trans-unit> </trans-unit>
<trans-unit id="jcT9sdfOg6" resname="electronic_invoice" xml:space="preserve"> <trans-unit id="N_ZNih_" resname="electronic_invoice" xml:space="preserve">
<source>electronic_invoice</source> <source>electronic_invoice</source>
<target state="translated">Factura electrónica</target> <target state="translated">Factura electrónica</target>
</trans-unit> </trans-unit>

View File

@@ -2042,7 +2042,7 @@
<source>buyerReference</source> <source>buyerReference</source>
<target state="final">זיהוי רוכש</target> <target state="final">זיהוי רוכש</target>
</trans-unit> </trans-unit>
<trans-unit id="jcT9sdfOg6" resname="electronic_invoice" xml:space="preserve" approved="yes"> <trans-unit id="N_ZNih_" resname="electronic_invoice" xml:space="preserve" approved="yes">
<source>electronic_invoice</source> <source>electronic_invoice</source>
<target state="final">חשבונית דיגיטלית</target> <target state="final">חשבונית דיגיטלית</target>
</trans-unit> </trans-unit>

View File

@@ -2042,7 +2042,7 @@
<source>payment_account_name</source> <source>payment_account_name</source>
<target state="translated">IBAN ili broj kartice</target> <target state="translated">IBAN ili broj kartice</target>
</trans-unit> </trans-unit>
<trans-unit id="jcT9sdfOg6" resname="electronic_invoice" xml:space="preserve" approved="no"> <trans-unit id="N_ZNih_" resname="electronic_invoice" xml:space="preserve" approved="no">
<source>electronic_invoice</source> <source>electronic_invoice</source>
<target state="translated">E-račun</target> <target state="translated">E-račun</target>
</trans-unit> </trans-unit>

View File

@@ -2042,7 +2042,7 @@
<source>payment_account_name</source> <source>payment_account_name</source>
<target state="final">IBAN o numero di carta</target> <target state="final">IBAN o numero di carta</target>
</trans-unit> </trans-unit>
<trans-unit id="jcT9sdfOg6" resname="electronic_invoice" xml:space="preserve" approved="yes"> <trans-unit id="N_ZNih_" resname="electronic_invoice" xml:space="preserve" approved="yes">
<source>electronic_invoice</source> <source>electronic_invoice</source>
<target state="final">Fattura elettronica</target> <target state="final">Fattura elettronica</target>
</trans-unit> </trans-unit>

View File

@@ -2038,7 +2038,7 @@
<source>payment_account_name</source> <source>payment_account_name</source>
<target state="translated">IBAN eller kortnummer</target> <target state="translated">IBAN eller kortnummer</target>
</trans-unit> </trans-unit>
<trans-unit id="jcT9sdfOg6" resname="electronic_invoice" xml:space="preserve" approved="no"> <trans-unit id="N_ZNih_" resname="electronic_invoice" xml:space="preserve" approved="no">
<source>electronic_invoice</source> <source>electronic_invoice</source>
<target state="translated">E-faktura</target> <target state="translated">E-faktura</target>
</trans-unit> </trans-unit>

View File

@@ -2014,7 +2014,7 @@
<source>payment_account_name</source> <source>payment_account_name</source>
<target state="translated">IBAN sau numar card</target> <target state="translated">IBAN sau numar card</target>
</trans-unit> </trans-unit>
<trans-unit id="jcT9sdfOg6" resname="electronic_invoice" xml:space="preserve" approved="no"> <trans-unit id="N_ZNih_" resname="electronic_invoice" xml:space="preserve" approved="no">
<source>electronic_invoice</source> <source>electronic_invoice</source>
<target state="translated">E-Factura</target> <target state="translated">E-Factura</target>
</trans-unit> </trans-unit>

View File

@@ -2042,7 +2042,7 @@
<source>payment_account_name</source> <source>payment_account_name</source>
<target state="translated">IBAN eller kortnummer</target> <target state="translated">IBAN eller kortnummer</target>
</trans-unit> </trans-unit>
<trans-unit id="jcT9sdfOg6" resname="electronic_invoice" xml:space="preserve"> <trans-unit id="N_ZNih_" resname="electronic_invoice" xml:space="preserve">
<source>electronic_invoice</source> <source>electronic_invoice</source>
<target state="translated">E-faktura</target> <target state="translated">E-faktura</target>
</trans-unit> </trans-unit>

View File

@@ -2042,7 +2042,7 @@
<source>payment_account_name</source> <source>payment_account_name</source>
<target state="translated">IBAN або номер картки</target> <target state="translated">IBAN або номер картки</target>
</trans-unit> </trans-unit>
<trans-unit id="jcT9sdfOg6" resname="electronic_invoice" xml:space="preserve" approved="no"> <trans-unit id="N_ZNih_" resname="electronic_invoice" xml:space="preserve" approved="no">
<source>electronic_invoice</source> <source>electronic_invoice</source>
<target state="translated">E-Invoice</target> <target state="translated">E-Invoice</target>
</trans-unit> </trans-unit>

View File

@@ -2717,9 +2717,9 @@ __metadata:
linkType: hard linkType: hard
"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001359, caniuse-lite@npm:^1.0.30001587, caniuse-lite@npm:^1.0.30001669": "caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001359, caniuse-lite@npm:^1.0.30001587, caniuse-lite@npm:^1.0.30001669":
version: 1.0.30001718 version: 1.0.30001755
resolution: "caniuse-lite@npm:1.0.30001718" resolution: "caniuse-lite@npm:1.0.30001755"
checksum: 10/e172a4c156f743cc947e659f353ad9edb045725cc109a02cc792dcbf98569356ebfa4bb4356e3febf87427aab0951c34c1ee5630629334f25ae6f76de7d86fd0 checksum: 10/67f3b87bfd8f4da6fd69df185f54ab5409171f62185a52c916e1eb2f70f853aa374b0ce75d1742cc0215ca61e4bd1da8aa5557081bb2b6bb7220bf03a19b3b6e
languageName: node languageName: node
linkType: hard linkType: hard