feat: due-date reminders
Every hour the API reminds whoever has a ticket, and whoever follows it, the day before its due date, on the day and once when it is overdue, in the app and by e-mail for those who asked. Cards on the board flag what is due soon or late. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,12 @@ import {
|
||||
} from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { PiIconComponent } from '@pi/ui';
|
||||
import { IssueSummary, Workflow } from '@task-stream/web/tracker/domain';
|
||||
import {
|
||||
DueState,
|
||||
dueState,
|
||||
IssueSummary,
|
||||
Workflow,
|
||||
} from '@task-stream/web/tracker/domain';
|
||||
import { initials, PRIORITY, PRIORITY_LEVEL, typeLook } from './copy';
|
||||
|
||||
/** A ticket on the board / in lists: type stripe, key, title, signals. */
|
||||
@@ -45,6 +50,14 @@ import { initials, PRIORITY, PRIORITY_LEVEL, typeLook } from './copy';
|
||||
>↑ {{ parent.key }}</span
|
||||
>
|
||||
}
|
||||
@if (due(); as state) {
|
||||
<span
|
||||
class="ts-card__due"
|
||||
[attr.data-due]="state"
|
||||
[attr.title]="'Fecha límite: ' + issue().dueDate"
|
||||
>{{ dueLabel(state) }}</span
|
||||
>
|
||||
}
|
||||
@if (issue().storyPoints !== null) {
|
||||
<span class="ts-card__points" title="Estimación">{{
|
||||
issue().storyPoints
|
||||
@@ -78,6 +91,25 @@ import { initials, PRIORITY, PRIORITY_LEVEL, typeLook } from './copy';
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
.ts-card__due {
|
||||
padding: 0 0.375rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--ts-c-amber, #d97706) 18%,
|
||||
transparent
|
||||
);
|
||||
&[data-due='overdue'] {
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--ts-c-red, #dc2626) 18%,
|
||||
transparent
|
||||
);
|
||||
color: var(--ts-c-red, inherit);
|
||||
}
|
||||
}
|
||||
.ts-card__points {
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
@@ -227,6 +259,16 @@ import { initials, PRIORITY, PRIORITY_LEVEL, typeLook } from './copy';
|
||||
`,
|
||||
})
|
||||
export class IssueCardComponent {
|
||||
/** Late, due today or in the next two days. */
|
||||
protected readonly due = computed(() =>
|
||||
dueState(this.issue().dueDate, this.issue().statusCategory),
|
||||
);
|
||||
protected dueLabel(state: DueState): string {
|
||||
if (state === 'overdue') return 'Vencido';
|
||||
if (state === 'today') return 'Vence hoy';
|
||||
const day = new Date(`${this.issue().dueDate?.slice(0, 10)}T00:00:00`);
|
||||
return day.toLocaleDateString('es', { day: 'numeric', month: 'short' });
|
||||
}
|
||||
readonly issue = input.required<IssueSummary>();
|
||||
/** The project's settings: names, icons and colours of types and areas. */
|
||||
readonly workflow = input.required<Workflow>();
|
||||
|
||||
Reference in New Issue
Block a user