Commit Graph

1618 Commits

Author SHA1 Message Date
Frederick [Bot] cf1273c1d9 chore(i18n): update translations via Crowdin 2026-06-29 00:45:18 +00:00
Frederick [Bot] 0dba563a03 chore(i18n): update translations via Crowdin 2026-06-28 00:29:43 +00:00
kolaente e25ca7ab9a fix: don't re-login after logout when OIDC auto-redirect is enabled
Set the just-logged-out flag before navigating, and skip the intermediate
router.push to login when redirecting to the IdP — otherwise Login.vue's
onBeforeMount consumed the flag before the logout round-trip landed, so the
single-provider auto-redirect fired and logged the user straight back in.

redirectToProviderOnLogout now reports whether it navigated, so logout can fall through to the login page when there's no static logout URL.
2026-06-27 14:20:05 +00:00
kolaente 18ee92f227 feat: auto-redirect to OIDC provider on login when it's the only option 2026-06-27 14:20:05 +00:00
BlackFuffey f18813f3ff feat(projects): make gantt chart zoom in if there are space available 2026-06-27 13:44:03 +00:00
gabe f7ac69d01a feat(filters): translate My Open Tasks title in frontend 2026-06-27 13:35:50 +00:00
kolaente 08890895de fix(task): don't drop the list-view done save during the check animation
Marking a task done via the list-view checkbox deferred the entire update
— including the network request — by 300ms to play the check animation. If
the page was torn down within that window (a refresh, tab close, or leaving
the app), the request was never sent and the save was silently lost. For
repeating tasks this is especially confusing: the due date never advances,
yet the checkbox un-checks itself anyway, so the failure looks identical to
success.

Fire the request immediately with the intended done value snapshotted, and
defer only the animation-coupled follow-up (result swap, pop sound, toast).
The optimistic v-model state already drives the check animation during the
300ms, so nothing visual is lost.
2026-06-27 12:01:51 +00:00
Frederick [Bot] 9390199ce0 chore(i18n): update translations via Crowdin 2026-06-26 00:32:25 +00:00
Frederick [Bot] aaa2428f6c chore(i18n): update translations via Crowdin 2026-06-24 00:26:43 +00:00
Tink f4bbe80144
fix(auth): dedupe and retry token refresh to prevent spurious logouts (#2948) 2026-06-21 18:22:30 +02:00
Frederick [Bot] 02d46944ac chore(i18n): update translations via Crowdin 2026-06-21 00:33:22 +00:00
kolaente 0e17556a16 fix(editor): make link prompt a sub-modal — Escape cancels it without closing the task dialog
Review point (#2950, comment 3444116036): when the surrounding task
<dialog> closed while the link prompt was open, the prompt was orphaned
and cleanup() never ran, leaking listeners and an unresolved promise.

Treat the prompt as a sub-modal of the task dialog: pressing Escape while
it is open now preventDefault()/stopPropagation()s the keydown so the
native modal <dialog> does not close on Escape, resolves the prompt with
'' (cancel) and runs cleanup() — only the prompt is dismissed, the task
dialog stays open. A one-shot 'cancel' listener on the enclosing dialog
backs this up in case the keydown handling is insufficient in some browser.

Tighten cleanup() so the prompt fully tears down regardless of how it
closes (Enter / Escape / click-outside): it now removes the scroll
listener, the document click listener and the dialog cancel listener, and
removes the element. handleClickOutside was hoisted so cleanup() can
remove it, closing the leaked-listener gap directly.

Adds an e2e asserting Escape cancels the prompt while the task dialog
stays open; the existing 'Enter creates the link' case still passes.
2026-06-19 20:14:19 +00:00
kolaente 84dc57c562 fix(editor): render link prompt inside the task dialog so it works in the Kanban popup (#2940)
The Kanban task popup renders the description editor inside a native
<dialog> opened via showModal(), which lives in the browser's top-layer.
inputPrompt appended its URL <input> to document.body, so it was painted
behind the top-layer dialog (z-index cannot beat the top-layer) and could
not be focused through the dialog's focus trap. As a result clicking "Link"
in the popup did nothing, while it worked on the full task page (no modal).

Thread the TipTap editor through inputPrompt and append the prompt to
getPopupContainer(editor) — the open dialog ancestor when present, falling
back to document.body otherwise, so non-modal usage is unchanged. This is
the same helper the slash menu and mentions already use to escape the
top-layer (#1746).

Fixes #2940
2026-06-19 20:14:19 +00:00
Tink 82dae774f1
fix(views): persist list/table sort across sidebar navigation (#2778) 2026-06-19 22:08:06 +02:00
kolaente 63b7f32379 fix(editor): render floating popups inside the task dialog (Kanban popup)
The Kanban task detail opens as a native <dialog> via showModal(), which
paints in the browser top-layer. Floating UI appended to document.body
(or teleported to <body>) then renders behind the dialog regardless of
z-index, matching the bug class of #2940 / #1746 / #1899 / #1929.

- Emoji autocomplete popup: append to getPopupContainer(editor) (the open
  dialog ancestor, else body), the same helper the slash menu and mentions
  already use. Also switch its unmount to popupElement.remove() so it works
  no matter which container it was appended to.
- Attachment dropzone overlay: teleport into the topmost open
  dialog.modal-dialog instead of always <body>, mirroring Notification.vue,
  so the drag-and-drop hint is visible while a task detail dialog is open.
2026-06-19 19:03:20 +00:00
Tink b6af132845
fix(auth): preserve desktop authorize URL when not signed in (#2944) 2026-06-19 19:50:47 +02:00
Tink 7208694960
fix(auth): build OIDC end-session URL with RP-Initiated Logout params (#2943) 2026-06-19 18:27:33 +02:00
kolaente 6e1b15e344 fix(tasks): add labels sequentially when the backend db serializes writes
Quick Add Magic with multiple labels (`*a *b *c`) fired all
`PUT /tasks/{id}/labels` requests concurrently via `Promise.all`. On
SQLite these overlap as read-then-write upgrade transactions, which the
busy_timeout can't resolve, so some requests fail with HTTP 500
("database is locked") and the labels are silently dropped while the
quick-add input gets stuck.

Expose a `concurrent_writes` flag on the shared `/info` response (true
for Postgres/MySQL, false for SQLite). The frontend config store reads
it and `addLabelsToTask` now branches: parallel `Promise.all` when the
backend supports concurrent writes, sequential awaits otherwise.

Fixes #2680
2026-06-19 14:19:19 +00:00
Tink f3c6312a9e
feat(projects): make duplicating shares opt-in (#2932) 2026-06-19 10:15:58 +02:00
Rashed Arman 5edc7b5160 fix: blur quick add input on escape 2026-06-18 21:42:21 +00:00
kolaente 9d18ba236f
feat(time-tracking): add favicon indicator for active time tracking sessions (#2937) 2026-06-18 23:52:52 +02:00
kolaente 86ec62d10b
fix(frontend): scroll tall default modals instead of clipping their top
Centered default/hint-modal content used translate(-50%, -50%) with no
height cap, so a taller-than-viewport modal (e.g. project background
settings with the Unsplash grid) pushed its top edge above the viewport
where the container's overflow can't reach it — the upload button became
unreachable on short screens.

Cap the centered content to the viewport and scroll inside it, mirroring
the height limit the .top (quick actions) variant already has. The mobile
breakpoint resets both so the fullscreen layout keeps flowing in
.modal-container.
2026-06-18 22:24:47 +02:00
kolaente 2cc7c0b6f0 fix(frontend): auto-refresh relative dates as time passes
Relative dates ("5 minutes ago", "in 2 hours") were computed once via
dayjs().fromNow() and never recomputed, so a view left open kept showing
the value from the moment it was rendered.

Compute the relative string against the shared, ticking `now` from
useGlobalNow() instead. This makes every reactive caller — <TimeDisplay>,
direct formatDateSince() calls, and formatDisplayDate() when the user's
date display is set to relative — re-render on the existing 60s tick.
Absolute date formats don't read `now`, so they never needlessly
re-render.

useGlobalNow can now be initialised from a plain helper rather than only
from a component, so its route-update hook is guarded with
getCurrentInstance().
2026-06-17 20:10:21 +00:00
kolaente 8a255cbff6 fix(gantt): preserve horizontal scroll when focusing a task bar
Focusing the task bar SVG `<g role="slider">` inside the
`overflow-x:auto` `.gantt-container` triggered Firefox's focus-induced
scroll-into-view, which jumped the scroll container back toward
`scrollLeft=0` (today). Pass `{ preventScroll: true }` to `focus()` so
selecting a bar keeps the current scroll position. Chromium scrolls
minimally on focus so it never manifested there.

Fixes #2728
2026-06-17 18:17:29 +00:00
Frederick [Bot] a8bce2ef0b chore(i18n): update translations via Crowdin 2026-06-17 00:35:30 +00:00
Frederick [Bot] 85b820fa7c chore(i18n): update translations via Crowdin 2026-06-16 00:40:29 +00:00
kolaente 8ff4696786 fix(frontend): restore quick actions menu styling and height limit
The quick actions menu (cmd+k) rendered without any background and grew
beyond the viewport:

- Its card visuals came from the global Bulma .card styles, which were
  dropped when Card.vue got its own scoped copy — QuickActions is the
  only place using a bare class="card" div, so it lost background,
  border and shadow. Give it its own card styles.
- Its height limit came from Bulma's .modal-content max-height, lost
  when the Bulma modal import was dropped in the native-dialog refactor.
  The :deep(.modal-content) position override in QuickActions never
  matched (.modal-content is an ancestor of the scoped selector, not a
  descendant). Replace both with a proper `top` modal variant that
  anchors the content 3rem below the top edge and caps its height,
  resolving the FIXME asking for exactly that option.
- The dark scrim never showed: Chromium intermittently stops painting a
  styled ::backdrop (after subtree re-renders, or while display is
  transitioned) even though getComputedStyle reports the color. Move
  the scrim onto the viewport-filling dialog element itself — same as
  the old div-based .modal-mask — and drop the display/allow-discrete
  transitions, which the JS-timed close fade never needed.
2026-06-12 07:26:17 +00:00
Frederick [Bot] f819b685d8 chore(i18n): update translations via Crowdin 2026-06-12 00:35:31 +00:00
Milad Nazari 1cf10b563a fix(frontend): fix buttons alignments in rtl direction 2026-06-11 19:45:57 +00:00
Milad Nazari adc8070ff9 feat(i18n): add persian to list of selectable languages 2026-06-11 19:45:57 +00:00
kolaente e16d120236 fix(time-tracking): cap smart-fill start at now so the range is never inverted
Smart-fill set the From time to the configured default start (09:00) when there
was no recent entry to continue from. Before that time of day the default lands
in the future, after the To time of now, producing an inverted range the backend
rejects (end_time before start_time). The save then failed silently and the
entry never appeared.

This surfaced as a flaky time-tracking e2e suite: the smart-fill specs failed
only when CI happened to run before 09:00 UTC.
2026-06-10 11:47:47 +02:00
Frederick [Bot] 8502c541a6 chore(i18n): update translations via Crowdin 2026-06-10 00:33:40 +00:00
Frederick [Bot] df6a56b195 chore(i18n): update translations via Crowdin 2026-06-09 00:26:57 +00:00
kolaente 00bbdbf95b refactor(time-tracking): drop the now-redundant duration clamp in the entry list 2026-06-08 13:54:09 +00:00
kolaente 2d334e56c7 i18n(time-tracking): add the time-tracking UI strings 2026-06-08 13:54:09 +00:00
kolaente 7c021dd663 feat(time-tracking): configure the smart-fill start time in settings 2026-06-08 13:54:09 +00:00
kolaente e948b191b0 feat(time-tracking): add the time-tracking view 2026-06-08 13:54:09 +00:00
kolaente 2ef898e89d feat(time-tracking): add the task-detail time-tracking section 2026-06-08 13:54:09 +00:00
kolaente 8febfac742 feat(time-tracking): add the sidebar navigation entry 2026-06-08 13:54:09 +00:00
kolaente 212d891fa1 feat(time-tracking): show a running-elsewhere badge in the header 2026-06-08 13:54:09 +00:00
kolaente 1832d0d3ee feat(time-tracking): add the timer badge 2026-06-08 13:54:09 +00:00
kolaente bb4f19da27 feat(time-tracking): add the time-entry list 2026-06-08 13:54:09 +00:00
kolaente ab8189e927 feat(time-tracking): add the time-entry form 2026-06-08 13:54:09 +00:00
kolaente 8c34676946 feat(time-tracking): extract the smart-fill start computation 2026-06-08 13:54:09 +00:00
kolaente e4b0a487fc feat(date): accept a null modelValue in DatepickerWithRange 2026-06-08 13:54:09 +00:00
kolaente 8839c296a2 feat(date): show the matching preset name on the date-range button 2026-06-08 13:54:09 +00:00
kolaente 27bb80d11a feat(input): add quick-select shortcuts to the Datepicker 2026-06-08 13:54:09 +00:00
kolaente 43d0203358 feat(time-tracking): add the time-tracking store 2026-06-08 13:54:09 +00:00
kolaente 80c21e6f40 feat(time-tracking): add the v2 time-entry service 2026-06-08 13:54:09 +00:00
kolaente 565bf97294 refactor(config): add PRO_FEATURE constants for licensed features 2026-06-08 13:54:09 +00:00