first commit

This commit is contained in:
2026-10-03 13:40:26 +02:00
parent 732a5b5a16
commit 566f415fa7
555 changed files with 62704 additions and 0 deletions

View File

@@ -0,0 +1,155 @@
import {
CommentAddedEvent,
CommentDeletedEvent,
CommentEditedEvent,
IssueAssignedEvent,
IssueCreatedEvent,
IssueLinkedEvent,
IssueLinkType,
IssueReleaseChangedEvent,
IssueStatus,
IssueStatusChangedEvent,
IssueType,
IssueUnlinkedEvent,
IssueUpdatedEvent,
ProjectCreatedEvent,
ProjectMemberAddedEvent,
ProjectMemberRemovedEvent,
ProjectMemberRoleChangedEvent,
ProjectRole,
ProjectUpdatedEvent,
ReleaseArchivedEvent,
ReleaseCreatedEvent,
ReleaseShippedEvent,
ReleaseUpdatedEvent,
TrackerEvent,
} from '@task-stream/api/tracker/domain';
export interface SummaryContext {
actor: string;
user: (id: string | null) => string;
issueKey: string;
projectKey: string;
releaseName: string;
}
/* The activity feed is read by people: Spanish, with the same labels as the web app. */
const STATUS: Record<IssueStatus, string> = {
[IssueStatus.BACKLOG]: 'Backlog',
[IssueStatus.TODO]: 'Por hacer',
[IssueStatus.IN_PROGRESS]: 'En curso',
[IssueStatus.IN_REVIEW]: 'En revisión',
[IssueStatus.READY_FOR_QA]: 'Listo para QA',
[IssueStatus.IN_QA]: 'En QA',
[IssueStatus.QA_FAILED]: 'QA fallido',
[IssueStatus.DONE]: 'Hecho',
[IssueStatus.CANCELLED]: 'Cancelado',
};
const TYPE: Record<IssueType, string> = {
[IssueType.BUG]: 'bug',
[IssueType.FEATURE]: 'feature',
[IssueType.UI_CHANGE]: 'cambio de UI',
[IssueType.TASK]: 'tarea',
[IssueType.QA]: 'QA',
};
const LINK: Record<IssueLinkType, string> = {
[IssueLinkType.BLOCKS]: 'bloquea a',
[IssueLinkType.DEPENDS_ON]: 'depende de',
[IssueLinkType.TESTS]: 'prueba',
[IssueLinkType.DUPLICATES]: 'duplica a',
[IssueLinkType.RELATES_TO]: 'está relacionado con',
};
const ROLE: Record<ProjectRole, string> = {
[ProjectRole.OWNER]: 'propietario',
[ProjectRole.ADMIN]: 'admin',
[ProjectRole.MEMBER]: 'miembro',
[ProjectRole.VIEWER]: 'lector',
};
const FIELD: Record<string, string> = {
title: 'el título',
description: 'la descripción',
type: 'el tipo',
area: 'el área',
priority: 'la prioridad',
labels: 'las etiquetas',
figmaUrl: 'el enlace de Figma',
storyPoints: 'los puntos',
dueDate: 'la fecha límite',
};
const list = (items: string[]) =>
items.length <= 1
? items.join('')
: `${items.slice(0, -1).join(', ')} y ${items[items.length - 1]}`;
/** One human-readable line per event, for the project activity feed. */
export function describeEvent(
event: TrackerEvent,
ctx: SummaryContext,
): string {
const { actor, issueKey } = ctx;
if (event instanceof ProjectCreatedEvent)
return `${actor} creó el proyecto ${event.project.key}`;
if (event instanceof ProjectUpdatedEvent)
return `${actor} actualizó el proyecto ${ctx.projectKey}`;
if (event instanceof ProjectMemberAddedEvent) {
return `${actor} añadió a ${ctx.user(event.userId)} como ${ROLE[event.role]}`;
}
if (event instanceof ProjectMemberRemovedEvent) {
return event.userId === event.actorId
? `${actor} salió del proyecto`
: `${actor} quitó a ${ctx.user(event.userId)} del proyecto`;
}
if (event instanceof ProjectMemberRoleChangedEvent) {
return `${actor} cambió el rol de ${ctx.user(event.userId)} a ${ROLE[event.role]}`;
}
if (event instanceof IssueCreatedEvent) {
return `${actor} creó ${event.issue.key} (${TYPE[event.issue.type]}): ${event.issue.title}`;
}
if (event instanceof IssueUpdatedEvent) {
return `${actor} cambió ${list(Object.keys(event.changes).map((f) => FIELD[f] ?? f))} de ${issueKey}`;
}
if (event instanceof IssueStatusChangedEvent) {
return `${actor} movió ${issueKey} de ${STATUS[event.from]} a ${STATUS[event.to]}${event.reason ? `: ${event.reason}` : ''}`;
}
if (event instanceof IssueAssignedEvent) {
return event.assigneeId
? `${actor} asignó ${issueKey} a ${ctx.user(event.assigneeId)}`
: `${actor} dejó ${issueKey} sin asignar`;
}
if (event instanceof IssueLinkedEvent) {
return `${actor} indicó que ${issueKey} ${LINK[event.link.type]} ${event.link.targetKey}`;
}
if (event instanceof IssueUnlinkedEvent) {
return `${actor} quitó la relación entre ${issueKey} y ${event.link.targetKey}`;
}
if (event instanceof IssueReleaseChangedEvent) {
return event.releaseId
? `${actor} metió ${issueKey} en la release ${ctx.releaseName}`
: `${actor} sacó ${issueKey} de su release`;
}
if (event instanceof CommentAddedEvent)
return `${actor} comentó en ${issueKey}`;
if (event instanceof CommentEditedEvent)
return `${actor} editó un comentario en ${issueKey}`;
if (event instanceof CommentDeletedEvent)
return `${actor} borró un comentario en ${issueKey}`;
if (event instanceof ReleaseCreatedEvent)
return `${actor} creó la release ${event.release.name}`;
if (event instanceof ReleaseUpdatedEvent)
return `${actor} actualizó la release ${ctx.releaseName}`;
if (event instanceof ReleaseShippedEvent)
return `${actor} publicó la release ${ctx.releaseName} 🚀`;
if (event instanceof ReleaseArchivedEvent)
return `${actor} archivó la release ${ctx.releaseName}`;
return `${actor}: ${event.constructor.name}`;
}