On narrow screens the temporary-note banner squeezed its text into a
one-word-per-line ladder and overflowing words slid under the subtle
"Move to trash" button. Two layout causes, both fixed here (layout-only; no
handler/logic/i18n changes):
- The text Group had `flex: 1` (= basis 0), so the outer `wrap="wrap"` never
wrapped the buttons to a second row — it crushed the text instead. Give it a
non-zero basis (`flex: 1 1 16rem`) so the wrap engages on narrow containers.
- Mirror DeletedPageBanner's adaptive actions: labeled Buttons visibleFrom="sm",
icon-only ActionIcon + Tooltip + aria-label hiddenFrom="sm" (same handlers,
loading flags, and t() keys). This also fixes the ru locale, whose long labels
no longer render on mobile.
The sibling DeletedPageBanner already uses this pattern; adding the second button
in #273/#277 didn't carry the adaptive part over.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The banner only offered 'Make permanent'. Add a secondary destructive
'Move to trash' button that soft-deletes the note now instead of waiting for
TTL expiry, reusing the tree/header soft-delete path (useTreeMutation.handleDelete):
optimistic tree removal, the undo-toast, the deletedAt cache stamp, and the
redirect to space home. No confirm modal (project convention = undo-toast).
Gated on the existing Edit permission. Client-only, no server/i18n changes
(both labels already exist).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Must-fix:
- CHANGELOG: add [Unreleased]/Added entry for temporary notes (#201).
- temporary-note-cleanup: re-check temporary_expires_at at deletion time so a
concurrent "Make permanent" (sets it NULL) between the batch SELECT and the
per-row removePage wins the race and the note is not trashed. Add unit tests
for the make-permanent and already-trashed race windows.
Non-blocking review items:
- temporary-note-cleanup: cap the sweep batch (LIMIT 500) so a large backlog is
not loaded into memory; remainder drains on the next hourly run.
- client: extract duplicated post-toggle cache sync into
syncTemporaryExpiresInCache() shared by the header menu and the banner.
- Remove the tautological migration spec that mocked the whole Kysely builder.
- Tests: cover create() frozen temporaryExpiresAt (workspace override + NULL
default fallback + non-temporary skips lookup) and restorePage disarming the
timer (temporaryExpiresAt: null).
Deferred (forward-looking, non-blocking): extract
PageService.computeTemporaryExpiresAt() to dedupe the deadline formula and drop
the @InjectKysely from PageTemplateController; replace migration unit test with
a real Postgres up/down integration test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
"Temporary notes" with a death timer: created via a dedicated hourglass button
in the space-tree header, a note auto-moves to Trash after a configurable X
hours (default 24) unless explicitly made permanent ("structure or die").
Reuses existing mechanisms, mirroring is_template and the trash-cleanup job:
- New nullable column pages.temporary_expires_at (NULL = permanent; non-NULL =
frozen deadline) + partial index for the sweep; workspace column
temporary_note_hours (default via DEFAULT_TEMPORARY_NOTE_HOURS = 24).
- create-page DTO `temporary` flag; the deadline is frozen at creation so later
setting changes never reschedule existing notes.
- POST /pages/toggle-temporary (mirror of toggle-template): arm/clear the timer,
CASL-guarded via validateCanEdit, cross-workspace NotFound defense-in-depth.
- TemporaryNoteCleanupService: hourly @Interval sweep that soft-deletes expired
notes through the exact PageRepo.removePage path (recursive over children,
emits PAGE_SOFT_DELETED), attributed to the creator; idempotent via
deletedAt IS NULL filters.
- restorePage clears temporary_expires_at so a restored note can't be re-trashed.
- Workspace setting temporary_note_hours (audit-tracked) + a hours editor in
workspace General settings.
- Client: second create button, orange tree icon, tree + page-header menu toggle
("Make temporary"/"Make permanent"), an open-note banner with a rescue action,
and en/ru i18n.
Tests (unit): toggle-temporary controller (toggle/explicit/permission/cross-ws +
DTO validation), cleanup-job sweep (selection filters, per-note removePage,
error isolation), and a migration up/down sanity. Server tsc, client tsc -b,
and the page+workspace jest suites are green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>