Skip to content

Multi-stop ticktack, TOOLBAR_PINNED event, and plugin entry filtering#5844

Open
ralf1070 wants to merge 3 commits into
kimai:devfrom
ralf1070:feature/multi-stop-toolbar-v3
Open

Multi-stop ticktack, TOOLBAR_PINNED event, and plugin entry filtering#5844
ralf1070 wants to merge 3 commits into
kimai:devfrom
ralf1070:feature/multi-stop-toolbar-v3

Conversation

@ralf1070

Copy link
Copy Markdown

Here is the public part of the project that led me to the off by one error yesterday.

It's the common part of a refactoring of what I considered to be ugly JS code, which was necessary to solve the task I was given: to display a stop button for every job running in parallel and to keep a dedicated start/stop button for a specific activity always available.

I hope you can use it - I'd be happy if you could. If you find the idea interesting but would implement it differently, let me know. I still have a few hours in my time budget for it. And if it's not suitable for mainline, that's okay too - it solves the task I was given and I can maintain it outside of mainline.

I know - parallel jobs aren't your favorite topic. But my colleagues are fans of them. Life is hard...

Summary

  • Ticktack renders hard_limit stop buttons instead of only the first active entry
  • New ThemeEvent::TOOLBAR_PINNED slot for plugins to place buttons before ticktack
  • New TicktackExcludeEvent allows plugins to filter entries out of ticktack
  • JS uses show/hide on pre-rendered slots - no DOM manipulation needed

Motivation

When timesheet.active_entries.hard_limit is set to more than 1, users can have multiple running timesheets simultaneously. However, ticktack in the navbar only shows the first active entry.

This change renders all hard_limit slots in the Twig template upfront. Active entries get real data, unused slots are hidden with display:none. The JS (KimaiActiveRecords._setEntries()) only toggles visibility and updates content via the existing _replaceInNode() mechanism.

TOOLBAR_PINNED event

For the job given I need to place a persistent action button in the navbar - in this case, a one-click attendance tracker that starts/stops a specific timesheet independently from the regular ticktack workflow.

The existing ThemeEvent::TOOLBAR renders after the ticktack and recent-activities. To position a button before the ticktack, plugins had to resort to JS-based DOM reordering.

TOOLBAR_PINNED provides a clean insertion point right before ticktack. Typical use case: a plugin manages a "special" timesheet (like attendance tracking) with its own start/stop button and wants it visually grouped with - but separate from - the regular ticktack buttons.

TicktackExcludeEvent

When a plugin manages a specific timesheet via its own button (rendered through TOOLBAR_PINNED), that entry would also appear as a regular stop button in ticktack - resulting in duplicate controls for the same entry.

TicktackExcludeEvent lets plugins declare which entries they manage themselves. Excluded entries are filtered out of ticktack both server-side (Twig initial render) and client-side (JS updates via data-exclude attribute). The total entry count including excluded entries is still used for the hard_limit check, so the start button correctly reflects the system's capacity.

Example: a plugin subscribes to the event and excludes entries matching a specific project + activity:

public function onTicktackExclude(TicktackExcludeEvent $event): void
{
    $event->addExclude(['project' => $projectId, 'activity' => $activityId]);
}

Each rule is an associative array where keys map to entry relations (project, activity) and values are entity IDs. An entry is excluded when all keys in a rule match. Multiple rules are OR-combined.

Changes

File Change
src/Event/ThemeEvent.php Add TOOLBAR_PINNED constant
src/Event/TicktackExcludeEvent.php New event for plugin-based entry filtering
templates/base.html.twig Trigger TOOLBAR_PINNED before ticktack include
src/Twig/Runtime/TimesheetExtension.php Inject SystemConfiguration + EventDispatcher, add activeEntriesHardLimit(), ticktackEntries(), ticktackExcludes()
src/Twig/RuntimeExtensions.php Register timesheet_hard_limit, ticktack_entries, ticktack_excludes Twig functions
templates/partials/ticktack.html.twig Pre-render hard_limit slots, use filtered entries for display, render data-exclude for JS
assets/js/plugins/KimaiActiveRecords.js _setEntries() reads data-exclude, filters display entries, uses total count for hard_limit
tests/Twig/Runtime/TimesheetExtensionTest.php Tests for hard limit, excludes (empty, with subscriber), filtered entries

Behavior

  • hard_limit=1 (default): Identical to current ticktack - one stop or one start button
  • hard_limit>1: Up to N stop buttons, each with project name tooltip. Start button hidden when limit reached. On mobile, a colored dot (project color) distinguishes the buttons.
  • Exclude filtering: Excluded entries don't appear in ticktack but still count toward the hard limit
  • Browser title: Shows duration of first entry only (via data-title="true" on slot 0)
  • Accesskeys: s on first stop button, n on start button (only when no entries active)
  • Dual navbar: Works in both mobile and desktop navbar instances

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • I verified that my code applies to the guidelines (composer code-check)
  • I updated the documentation (see here)
  • I agree that this code is used in Kimai (see license)

@ralf1070 ralf1070 force-pushed the feature/multi-stop-toolbar-v3 branch from dc4f17a to d6df0b6 Compare March 2, 2026 15:56
ralf1070 added 3 commits June 5, 2026 17:12
Render up to hard_limit stop buttons in the ticktack navbar. JS uses
show/hide on pre-rendered slots instead of replacing a single button.

Add ThemeEvent::TOOLBAR_PINNED slot before the ticktack for plugin
buttons (e.g. attendance).

Add TicktackExcludeEvent allowing plugins to exclude specific entries
from the ticktack (both server-side Twig and client-side JS via
data-exclude attribute on .ticktac).
Renders hard_limit - excludes.length slots instead of hard_limit,
so plugin-managed entries (e.g. Attendance) don't consume visible
ticktack slots. Start button hides when all visible slots are filled.
@ralf1070 ralf1070 force-pushed the feature/multi-stop-toolbar-v3 branch from d6df0b6 to c065ca3 Compare June 5, 2026 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant