feat: uuid ids everywhere, pickers for linked tickets, @pi/ui checkboxes

- Postgres: every table has a uuid `id` (gen_random_uuid()); the event
  store keeps its relay order in `seq`, and the lookup tables their
  uniqueness in constraints of their own.
- MongoDB: `_id` is Mongo's ObjectId; `id` is the domain id (the same
  uuid as the aggregate's stream in Postgres), unique, and every query
  goes by it. Production starts from empty databases.
- Web: linking tickets and hanging one from a parent are picked from a
  searchable select (links across projects, parents within the project)
  instead of typing a key; every checkbox is @pi/ui's pi-checkbox.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 19:16:43 +02:00
parent 10c8f21ff4
commit 2c501a50d0
30 changed files with 292 additions and 207 deletions

View File

@@ -9,6 +9,7 @@ import {
} from '@angular/core';
import {
PiActionButtonComponent,
PiCheckboxComponent,
PiSelectFieldComponent,
PiTextFieldComponent,
type PiSelectFieldOption,
@@ -29,21 +30,26 @@ const NOBODY = '__none__';
selector: 'ts-bulk-bar',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
PiCheckboxComponent,
PiActionButtonComponent,
PiSelectFieldComponent,
PiTextFieldComponent,
],
template: `
<div class="ts-bulk" role="toolbar" aria-label="Edición en bloque">
<label class="ts-bulk__all">
<input
type="checkbox"
[checked]="allSelected()"
[indeterminate]="count() > 0 && !allSelected()"
(change)="selectAll.emit($any($event.target).checked)"
/>
{{ count() ? count() + ' seleccionados' : 'Seleccionar todos' }}
</label>
<pi-checkbox
class="ts-bulk__all"
[checked]="allSelected()"
[indeterminate]="count() > 0 && !allSelected()"
[label]="
count() === 1
? '1 seleccionado'
: count()
? count() + ' seleccionados'
: 'Seleccionar todos'
"
(checkedChange)="selectAll.emit($event)"
/>
@if (count()) {
<pi-select-field [options]="fields" [(value)]="field" />
@switch (field()) {

View File

@@ -9,7 +9,7 @@ import {
untracked,
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { PiTextFieldComponent } from '@pi/ui';
import { PiCheckboxComponent, PiTextFieldComponent } from '@pi/ui';
import { ToastService } from '@task-stream/web/shared/ui';
import { Webhook, WebhooksGateway } from '@task-stream/web/tracker/domain';
import { timeAgo } from './copy';
@@ -38,7 +38,7 @@ const EVENT_LABEL: Record<string, string> = {
@Component({
selector: 'ts-webhooks',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [PiTextFieldComponent],
imports: [PiCheckboxComponent, PiTextFieldComponent],
styleUrl: './settings-card.scss',
template: `
<section class="ts-panel ts-block ts-hooks" aria-labelledby="hooks-title">
@@ -113,24 +113,18 @@ const EVENT_LABEL: Record<string, string> = {
<pi-text-field placeholder="https://…" [(value)]="url" />
<fieldset class="ts-hooks__picks">
<legend>Eventos</legend>
<label>
<input
type="checkbox"
[checked]="picked().includes('*')"
(change)="pick('*', $any($event.target).checked)"
/>
Todos
</label>
<pi-checkbox
label="Todos"
[checked]="picked().includes('*')"
(checkedChange)="pick('*', $event)"
/>
@for (event of events(); track event) {
<label>
<input
type="checkbox"
[disabled]="picked().includes('*')"
[checked]="picked().includes(event)"
(change)="pick(event, $any($event.target).checked)"
/>
{{ label(event) }}
</label>
<pi-checkbox
[label]="label(event)"
[disabled]="picked().includes('*')"
[checked]="picked().includes(event)"
(checkedChange)="pick(event, $event)"
/>
}
</fieldset>
<div>
@@ -214,11 +208,6 @@ const EVENT_LABEL: Record<string, string> = {
margin-bottom: 0.25rem;
color: var(--ts-muted);
}
label {
display: flex;
gap: 0.375rem;
align-items: center;
}
}
`,
})