A toast offers to undo for seven seconds (or Ctrl/⌘ Z outside a field) by running the opposite command: the card back to its column or the ticket to its status (the workflow may refuse it), the previous assignee, or the ticket back from the trash. The undo itself only says «Deshecho». On the board, from the card's assignee; on the ticket page, status, assignee and trash, which no longer asks for confirmation. The toast is the app's own (UndoToastComponent): pi-snack-bar cannot tell its button from the timeout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
70 lines
2.0 KiB
TypeScript
70 lines
2.0 KiB
TypeScript
import { type } from '@ngrx/signals';
|
|
import { eventGroup } from '@ngrx/signals/events';
|
|
import {
|
|
BoardColumn,
|
|
BoardFilter,
|
|
CommandResult,
|
|
CreateIssueInput,
|
|
IssueChanged,
|
|
IssuePriority,
|
|
SavedFilter,
|
|
} from '@task-stream/web/tracker/domain';
|
|
|
|
/** What a card can change without opening the ticket. */
|
|
export type CardChange =
|
|
| { priority: IssuePriority }
|
|
| { storyPoints: number | null }
|
|
| { assigneeId: string | null };
|
|
|
|
export const boardPageEvents = eventGroup({
|
|
source: 'Board Page',
|
|
events: {
|
|
opened: type<BoardFilter>(),
|
|
filterChanged: type<BoardFilter>(),
|
|
/** Optimistic: the card moves immediately and comes back if the API refuses. */
|
|
issueMoved: type<{
|
|
issueId: string;
|
|
to: string;
|
|
reason?: string | null;
|
|
/** Where it was: «Deshacer» takes it back. */
|
|
from?: string;
|
|
/** It is the «Deshacer» of a move: no undo for it. */
|
|
undoing?: boolean;
|
|
}>(),
|
|
issueCreateRequested: type<CreateIssueInput>(),
|
|
/** From the card: priority, estimate or assignee (the live update repaints it). */
|
|
issueEditRequested: type<{
|
|
issueId: string;
|
|
change: CardChange;
|
|
/** It is the «Deshacer» of a change: no undo for it. */
|
|
undoing?: boolean;
|
|
}>(),
|
|
/** Save the current filter under a name. */
|
|
viewSaveRequested: type<{ name: string; shared: boolean }>(),
|
|
viewDeleteRequested: type<{ id: string }>(),
|
|
},
|
|
});
|
|
|
|
export const boardApiEvents = eventGroup({
|
|
source: 'Board API',
|
|
events: {
|
|
loaded: type<BoardColumn[]>(),
|
|
loadFailed: type<string>(),
|
|
issueMoveFailed: type<string>(),
|
|
issueEditFailed: type<string>(),
|
|
issueCreated: type<CommandResult>(),
|
|
issueCreateFailed: type<string>(),
|
|
viewsLoaded: type<SavedFilter[]>(),
|
|
viewSaved: type<string>(),
|
|
viewDeleted: type<string>(),
|
|
viewFailed: type<string>(),
|
|
},
|
|
});
|
|
|
|
export const boardRealtimeEvents = eventGroup({
|
|
source: 'Board Realtime',
|
|
events: {
|
|
issueChanged: type<IssueChanged>(),
|
|
},
|
|
});
|