diff --git a/apps/api/openapi.json b/apps/api/openapi.json
index 92cae59..1869fb5 100644
--- a/apps/api/openapi.json
+++ b/apps/api/openapi.json
@@ -15066,7 +15066,7 @@
"path": {
"type": "string",
"nullable": true,
- "example": "/p/WEB/proponer",
+ "example": "/p/WEB/propose",
"description": "The form in the web app, while it takes them"
},
"top": {
diff --git a/apps/mcp/src/app/api/taskstream.schema.ts b/apps/mcp/src/app/api/taskstream.schema.ts
index 0fd9df1..c86878d 100644
--- a/apps/mcp/src/app/api/taskstream.schema.ts
+++ b/apps/mcp/src/app/api/taskstream.schema.ts
@@ -3012,7 +3012,7 @@ export interface components {
open: boolean;
/**
* @description The form in the web app, while it takes them
- * @example /p/WEB/proponer
+ * @example /p/WEB/propose
*/
path: string | null;
/** @description Open proposals, most voted first */
diff --git a/apps/web/src/app/app.routes.spec.ts b/apps/web/src/app/app.routes.spec.ts
index 5c37eb6..1f2ed27 100644
--- a/apps/web/src/app/app.routes.spec.ts
+++ b/apps/web/src/app/app.routes.spec.ts
@@ -12,6 +12,8 @@ describe('appRoutes', () => {
const children = app?.children ?? [];
expect(children.map((route) => route.path)).toEqual([
'',
+ 'portfolio',
+ 'explore',
'cartera',
'explorar',
'p/:key',
@@ -19,7 +21,10 @@ describe('appRoutes', () => {
expect(children[0].canActivate).toContain(authGuard);
expect(children[1].canActivate).toContain(authGuard);
expect(children[2].canActivate).toBeUndefined();
- expect(children[3].canActivate).toBeUndefined();
+ expect(children[5].canActivate).toBeUndefined();
+ // The Spanish paths of shared links lead to the English ones.
+ expect(children[3].redirectTo).toBe('portfolio');
+ expect(children[4].redirectTo).toBe('explore');
});
it('exposes every project section', () => {
@@ -28,8 +33,9 @@ describe('appRoutes', () => {
?.children?.find((route) => route.path === 'p/:key');
expect(project?.children?.map((route) => route.path)).toEqual([
'',
- 'board',
'proponer',
+ 'board',
+ 'propose',
'list',
'issues/:issueKey',
'backlog',
diff --git a/apps/web/src/app/app.ts b/apps/web/src/app/app.ts
index 229c7bb..be820e7 100644
--- a/apps/web/src/app/app.ts
+++ b/apps/web/src/app/app.ts
@@ -45,7 +45,7 @@ import {
Cartera
@@ -54,7 +54,7 @@ import {
Explorar
diff --git a/docs/03-api.md b/docs/03-api.md
index 4bc39c9..94bb170 100644
--- a/docs/03-api.md
+++ b/docs/03-api.md
@@ -70,7 +70,7 @@ lee igual que un anónimo. Las subscriptions exigen sesión; con ella, también
en el primer estado, de tipo Bug o Feature (o el primer tipo activo). No se pide correo, porque quedaría a la vista. Contra
el spam, el campo `website` es una trampa (si llega relleno se responde como si nada y no se guarda) y cada dirección
envía como mucho 5 cada 15 minutos (429); el contador vive en Redis (`RateLimitStore`), así que vale igual con
-varias instancias. Los propietarios y admins del proyecto reciben un aviso (`PROPOSED`). La web tiene el formulario en `/p/{CLAVE}/proponer`, enlazado en la cabecera del proyecto.
+varias instancias. Los propietarios y admins del proyecto reciben un aviso (`PROPOSED`). La web tiene el formulario en `/p/{CLAVE}/propose`, enlazado en la cabecera del proyecto.
**Votos**: quien tenga sesión vota los tickets de un proyecto público, y los miembros los de uno privado
(`voteIssue(issueId, on)`, `PUT/DELETE /api/issues/{issue}/vote`). Cada ticket da `votes` y `voted` (si he votado
diff --git a/docs/04-cliente-web.md b/docs/04-cliente-web.md
index 53a7463..13ae381 100644
--- a/docs/04-cliente-web.md
+++ b/docs/04-cliente-web.md
@@ -270,7 +270,7 @@ visualización de la plataforma:
### Proyectos públicos y visitantes sin sesión
-- Solo la portada (`/`) exige sesión. `/explorar` y las páginas de proyecto (`/p/:key/…`) no: la API decide qué ve
+- Solo la portada (`/`) exige sesión. `/explore` y las páginas de proyecto (`/p/:key/…`) no: la API decide qué ve
cada visitante, y las páginas ya ocultan lo que no puede cambiar (`canContribute`, `canManage`).
- Sin sesión, la cabecera muestra «Explorar» e «Iniciar sesión»; dentro de un proyecto público aparece un aviso de modo
lectura, y en uno privado (o inexistente), el enlace para iniciar sesión volviendo a esa página (`returnUrl`).
@@ -287,8 +287,8 @@ visualización de la plataforma:
| `/login` | Inicio de sesión (solo sin sesión) |
| `/auth/callback` | Retorno de Pireon (PKCE) |
| `/` | Inicio: saludo según la hora con el resumen del día, «Ir a…» (`WEB-12`), primeros pasos, cuatro cifras (`ts-home-kpis`: abiertos míos, esperando QA, terminados por mí y por el equipo esta semana), la semana (`ts-week-agenda`), mis vistas guardadas y dos columnas: mis tickets y lo último en mis proyectos; sprints en curso, esperando QA, que sigo, próximas releases, quién está ahora y «Retomar». Debajo, mis proyectos con su actividad |
-| `/cartera` | Cartera (con sesión): una tarjeta por proyecto activo con lo abierto y en riesgo, el sprint en curso (¿llega?), las próximas releases con su previsión y las épicas abiertas |
-| `/explorar` | Proyectos públicos (sin sesión) |
+| `/portfolio` | Cartera (con sesión): una tarjeta por proyecto activo con lo abierto y en riesgo, el sprint en curso (¿llega?), las próximas releases con su previsión y las épicas abiertas; en directo: se vuelve a leer sola cuando cambia un ticket, sprint o release de esos proyectos |
+| `/explore` | Proyectos públicos (sin sesión) |
| `/p/:key/board` | Tablero kanban (`?release=` y `?assignee=` preseleccionan los filtros) |
| `/p/:key/issues/:issueKey` | Ticket. Encima del título, quién más lo está viendo o editando ahora (`PresenceFacade`: late cada 10 s; editar es tener abierto el formulario del ticket o de un comentario, o estar escribiendo uno) |
| `/p/:key/releases` | Releases |
@@ -296,10 +296,12 @@ visualización de la plataforma:
| `/p/:key/roadmap` | Hoja de ruta: épicas en el tiempo, con sprints y releases; flechas «bloquea» / «depende de» entre épicas y aviso si una empieza antes de que acabe la que la bloquea |
| `/p/:key/dashboard` | Seguimiento: cifras, flujo por estado, lo que ha cambiado desde tu última visita (guardada en este navegador), sprint en curso, tickets en riesgo, previsión de las próximas releases, antigüedad de lo que está en curso, cuellos de botella, salud del backlog (cada número abre la lista filtrada: `?missing=estimate`, `area`, `assignee` o `?staleDays=90`), calidad (bugs por prioridad y por semana, QA por área, bugs tras cada release), abiertos por un campo de lista a elegir, equipo con sus puntos abiertos y tipos. Se recarga solo con cada cambio |
| `/p/:key/analytics` | Estadísticas: creados vs. resueltos, flujo acumulado, burnup de una release, tiempos de ciclo y entrega, QA y resueltos por persona (30 días a 1 año) |
-| `/p/:key/settings` | Configuración (propietario y admins; pestaña oculta para el resto), con un menú de secciones (`?seccion=`): General (visibilidad, propuestas), Flujo de trabajo (estados, transiciones, definición de «Hecho»), Tickets (tipos, áreas, campos), Equipos, Automatizaciones, Integraciones (Git, webhooks), Importar y exportar y Zona peligrosa; archivar en lugar de borrar, y un punto en el menú donde hay cambios sin guardar |
+| `/p/:key/settings` | Configuración (propietario y admins; pestaña oculta para el resto), con un menú de secciones (`?section=`): General (visibilidad, propuestas), Flujo de trabajo (estados, transiciones, definición de «Hecho»), Tickets (tipos, áreas, campos), Equipos, Automatizaciones, Integraciones (Git, webhooks), Importar y exportar y Zona peligrosa; archivar en lugar de borrar, y un punto en el menú donde hay cambios sin guardar |
| `/p/:key/activity` | Actividad en vivo |
+| `/p/:key/propose` | Formulario público para reportar un error o proponer una idea sin cuenta (si el proyecto lo tiene abierto); llegan como tickets con la etiqueta «propuesta» |
| `/p/:key/members` | Miembros, con su rol (propietario, admin, miembro, lector o invitado). Un invitado de un proyecto privado solo tiene Tablero, Lista y Miembros, ve solo sus tickets y abre tickets sin asignarlos ni planificarlos |
Solo `/` está protegida por `authGuard` (redirige a `/login?returnUrl=…`); `/login` y `/auth/callback` son para quien
no tiene sesión, y el resto se puede abrir sin ella si el proyecto es público. Los parámetros de ruta llegan como inputs de
-los componentes (`withComponentInputBinding`).
+los componentes (`withComponentInputBinding`). Las rutas van en inglés; las antiguas en castellano (`/cartera`,
+`/explorar`, `/p/:key/proponer`) redirigen a las nuevas, para que sigan valiendo los enlaces ya compartidos.
diff --git a/docs/10-futuro.md b/docs/10-futuro.md
index 3520aec..9f62967 100644
--- a/docs/10-futuro.md
+++ b/docs/10-futuro.md
@@ -121,7 +121,7 @@ Mucho trabajo para lo que aportan hoy; se pueden recuperar si alguien los necesi
- Presencia: en cada ticket se ve quién más lo está viendo o editando en ese momento.
- Rol «invitado»: para clientes o gente de fuera; en un proyecto privado solo ve los tickets en los que participa,
abre tickets y comenta en los suyos.
-- Vista de cartera (`/cartera`): todos tus proyectos activos de un vistazo, con su riesgo, sprint, releases y épicas.
+- Vista de cartera (`/portfolio`): todos tus proyectos activos de un vistazo, con su riesgo, sprint, releases y épicas.
- Una portada con vida: saludo y resumen del día, la semana que viene, lo último en tus proyectos, quién está ahora,
la semana en cifras (tuya y del equipo, con racha), sprints en curso, «Retomar», tarjetas de proyecto con actividad,
qué coger cuando no tienes nada y primeros pasos para quien empieza. También por MCP (`get_my_week`).
diff --git a/docs/TaskStream-Documentacion.docx b/docs/TaskStream-Documentacion.docx
index 341092e..9d1eec1 100644
Binary files a/docs/TaskStream-Documentacion.docx and b/docs/TaskStream-Documentacion.docx differ
diff --git a/docs/TaskStream-Documentacion.pdf b/docs/TaskStream-Documentacion.pdf
index 7daef32..d8d66f3 100644
Binary files a/docs/TaskStream-Documentacion.pdf and b/docs/TaskStream-Documentacion.pdf differ
diff --git a/libs/api/tracker/application/src/lib/queries/get-showcase.query.ts b/libs/api/tracker/application/src/lib/queries/get-showcase.query.ts
index d48e04e..9aa34a3 100644
--- a/libs/api/tracker/application/src/lib/queries/get-showcase.query.ts
+++ b/libs/api/tracker/application/src/lib/queries/get-showcase.query.ts
@@ -169,7 +169,7 @@ export class GetShowcaseQueryHandler
})),
proposals: {
open: project.acceptsProposals,
- path: project.acceptsProposals ? `/p/${project.key}/proponer` : null,
+ path: project.acceptsProposals ? `/p/${project.key}/propose` : null,
top: [...proposals.items]
.sort((a, b) => b.votes - a.votes)
.slice(0, 10)
diff --git a/libs/api/tracker/presenters/src/lib/http/dto/responses.dto.ts b/libs/api/tracker/presenters/src/lib/http/dto/responses.dto.ts
index 9d3358c..2a91fa4 100644
--- a/libs/api/tracker/presenters/src/lib/http/dto/responses.dto.ts
+++ b/libs/api/tracker/presenters/src/lib/http/dto/responses.dto.ts
@@ -361,7 +361,7 @@ class ShowcaseProposalsDto {
@ApiProperty({
type: String,
nullable: true,
- example: '/p/WEB/proponer',
+ example: '/p/WEB/propose',
description: 'The form in the web app, while it takes them',
})
path!: string | null;
diff --git a/libs/web/identity/presenters/src/lib/pages/login.page.ts b/libs/web/identity/presenters/src/lib/pages/login.page.ts
index 98359cf..bed720c 100644
--- a/libs/web/identity/presenters/src/lib/pages/login.page.ts
+++ b/libs/web/identity/presenters/src/lib/pages/login.page.ts
@@ -113,7 +113,7 @@ const SAMPLE_FEED = [
¿Solo quieres mirar?
- Proyectos públicos →
+ Proyectos públicos →
diff --git a/libs/web/tracker/application/src/lib/events/portfolio.events.ts b/libs/web/tracker/application/src/lib/events/portfolio.events.ts
index f7482f2..40702a7 100644
--- a/libs/web/tracker/application/src/lib/events/portfolio.events.ts
+++ b/libs/web/tracker/application/src/lib/events/portfolio.events.ts
@@ -16,3 +16,11 @@ export const portfolioApiEvents = eventGroup({
loadFailed: type
(),
},
});
+
+/** Something changed in one of my projects: reload, quietly. */
+export const portfolioLiveEvents = eventGroup({
+ source: 'Portfolio Live',
+ events: {
+ changed: type(),
+ },
+});
diff --git a/libs/web/tracker/application/src/lib/stores/portfolio.store.ts b/libs/web/tracker/application/src/lib/stores/portfolio.store.ts
index bd449d3..87435fc 100644
--- a/libs/web/tracker/application/src/lib/stores/portfolio.store.ts
+++ b/libs/web/tracker/application/src/lib/stores/portfolio.store.ts
@@ -10,10 +10,18 @@ import {
import {
PortfolioProject,
ProjectsGateway,
+ TrackerRealtimeGateway,
} from '@task-stream/web/tracker/domain';
-import { switchMap } from 'rxjs';
+import {
+ debounceTime,
+ distinctUntilChanged,
+ map,
+ merge,
+ switchMap,
+} from 'rxjs';
import {
portfolioApiEvents,
+ portfolioLiveEvents,
portfolioPageEvents,
} from '../events/portfolio.events';
import { describeError } from '../notifications';
@@ -25,7 +33,7 @@ interface PortfolioState {
error: string | null;
}
-/** All my active projects at a glance. Scoped to the portfolio page. */
+/** All my active projects at a glance, live. Scoped to the portfolio page. */
export const PortfolioStore = signalStore(
withState({
projects: [],
@@ -47,17 +55,46 @@ export const PortfolioStore = signalStore(
})),
),
withEventHandlers(
- (_store, events = inject(Events), projects = inject(ProjectsGateway)) => ({
- load$: events.on(portfolioPageEvents.opened).pipe(
- switchMap(() =>
- projects.portfolio().pipe(
- mapResponse({
- next: (list) => portfolioApiEvents.loaded(list),
- error: (error) =>
- portfolioApiEvents.loadFailed(describeError(error)),
- }),
+ (
+ _store,
+ events = inject(Events),
+ projects = inject(ProjectsGateway),
+ realtime = inject(TrackerRealtimeGateway),
+ ) => ({
+ // Opened, or something changed (then quietly: no «loading»).
+ load$: events
+ .on(portfolioPageEvents.opened, portfolioLiveEvents.changed)
+ .pipe(
+ switchMap(() =>
+ projects.portfolio().pipe(
+ mapResponse({
+ next: (list) => portfolioApiEvents.loaded(list),
+ error: (error) =>
+ portfolioApiEvents.loadFailed(describeError(error)),
+ }),
+ ),
),
),
+ // Any ticket, sprint or release of the projects shown, the list of my
+ // projects itself, or a cut connection: read it again, calmly.
+ live$: events.on(portfolioApiEvents.loaded).pipe(
+ map(({ payload }) => payload.map((p) => p.projectId).join(',')),
+ distinctUntilChanged(),
+ switchMap((ids) =>
+ merge(
+ ...ids
+ .split(',')
+ .filter(Boolean)
+ .flatMap((id) => [
+ realtime.issueChanges(id),
+ realtime.sprintChanges(id),
+ realtime.releaseChanges(id),
+ ]),
+ realtime.myProjectsChanges(),
+ realtime.reconnects(),
+ ).pipe(debounceTime(1500)),
+ ),
+ map(() => portfolioLiveEvents.changed()),
),
}),
),
diff --git a/libs/web/tracker/domain/src/lib/models/shortcuts.spec.ts b/libs/web/tracker/domain/src/lib/models/shortcuts.spec.ts
index 04e15fe..bdedef8 100644
--- a/libs/web/tracker/domain/src/lib/models/shortcuts.spec.ts
+++ b/libs/web/tracker/domain/src/lib/models/shortcuts.spec.ts
@@ -3,7 +3,7 @@ import { GO_TO, isTyping, projectKeyIn } from './shortcuts';
describe('shortcuts', () => {
it('knows the project of a URL', () => {
expect(projectKeyIn('/p/WEB/issues/WEB-3')).toBe('WEB');
- expect(projectKeyIn('/explorar')).toBeNull();
+ expect(projectKeyIn('/explore')).toBeNull();
expect(GO_TO['k']).toEqual({ path: 'backlog', inProject: true });
});
diff --git a/libs/web/tracker/domain/src/lib/models/shortcuts.ts b/libs/web/tracker/domain/src/lib/models/shortcuts.ts
index b9bef9f..36ea464 100644
--- a/libs/web/tracker/domain/src/lib/models/shortcuts.ts
+++ b/libs/web/tracker/domain/src/lib/models/shortcuts.ts
@@ -20,7 +20,7 @@ export const SHORTCUTS: { keys: string; what: string; where: string }[] = [
/** Where `g` + key goes; project pages are relative to `/p/KEY`. */
export const GO_TO: Record = {
p: { path: '/', inProject: false },
- e: { path: '/explorar', inProject: false },
+ e: { path: '/explore', inProject: false },
b: { path: 'board', inProject: true },
k: { path: 'backlog', inProject: true },
s: { path: 'sprints', inProject: true },
diff --git a/libs/web/tracker/presenters/src/lib/pages/explore.page.ts b/libs/web/tracker/presenters/src/lib/pages/explore.page.ts
index 6796578..49d0fdc 100644
--- a/libs/web/tracker/presenters/src/lib/pages/explore.page.ts
+++ b/libs/web/tracker/presenters/src/lib/pages/explore.page.ts
@@ -14,7 +14,7 @@ import { AuthFacade } from '@task-stream/web/identity/application';
import { ExploreFacade } from '@task-stream/web/tracker/application';
import { ProjectCardComponent } from '../ui/project-card';
-/** /explorar — public projects, for anyone (no sign-in needed). */
+/** /explore — public projects, for anyone (no sign-in needed). */
@Component({
selector: 'ts-explore-page',
changeDetection: ChangeDetectionStrategy.OnPush,
diff --git a/libs/web/tracker/presenters/src/lib/pages/portfolio.page.ts b/libs/web/tracker/presenters/src/lib/pages/portfolio.page.ts
index 9443020..4cc8105 100644
--- a/libs/web/tracker/presenters/src/lib/pages/portfolio.page.ts
+++ b/libs/web/tracker/presenters/src/lib/pages/portfolio.page.ts
@@ -48,14 +48,13 @@ const short = (day: string | null) =>
riesgo, el sprint en curso, las próximas releases y las épicas.
-
+
+ En directo
+
@if (portfolio.error(); as error) {
@@ -201,6 +200,33 @@ const short = (day: string | null) =>
gap: 1rem;
flex-wrap: wrap;
}
+ .ts-live {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin: 0;
+ color: var(--ts-muted);
+ font-size: 0.8125rem;
+ font-weight: 600;
+ }
+ .ts-live__dot {
+ width: 0.5rem;
+ height: 0.5rem;
+ border-radius: 50%;
+ background: var(--ts-c-green);
+ box-shadow: 0 0 0 0 color-mix(in srgb, var(--ts-c-green) 50%, transparent);
+ animation: ts-live-pulse 2s ease-out infinite;
+ }
+ @keyframes ts-live-pulse {
+ to {
+ box-shadow: 0 0 0 0.5rem transparent;
+ }
+ }
+ @media (prefers-reduced-motion: reduce) {
+ .ts-live__dot {
+ animation: none;
+ }
+ }
.ts-portfolio {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(100%, 26rem), 1fr));
diff --git a/libs/web/tracker/presenters/src/lib/pages/project-shell.page.ts b/libs/web/tracker/presenters/src/lib/pages/project-shell.page.ts
index 87c0688..9c681f1 100644
--- a/libs/web/tracker/presenters/src/lib/pages/project-shell.page.ts
+++ b/libs/web/tracker/presenters/src/lib/pages/project-shell.page.ts
@@ -42,7 +42,7 @@ import { projectTone, PUBLIC_TONE, ROLE } from '../ui/copy';
@if (auth.isAuthenticated()) {
Proyectos /
} @else {
-
Explorar /
+
Explorar /
}
{{ project.key }}
@if (facade.myRole(); as role) {
@@ -62,7 +62,7 @@ import { projectTone, PUBLIC_TONE, ROLE } from '../ui/copy';
}
@if (project.isPublic && project.acceptsProposals) {
-
}
@@ -150,7 +150,7 @@ import { projectTone, PUBLIC_TONE, ROLE } from '../ui/copy';
>Iniciar sesión
·
-
Ver proyectos públicos
diff --git a/libs/web/tracker/presenters/src/lib/pages/propose.page.ts b/libs/web/tracker/presenters/src/lib/pages/propose.page.ts
index 6e21c21..6bb625e 100644
--- a/libs/web/tracker/presenters/src/lib/pages/propose.page.ts
+++ b/libs/web/tracker/presenters/src/lib/pages/propose.page.ts
@@ -20,7 +20,7 @@ import { ProposalResult } from '@task-stream/web/tracker/domain';
import { RichEditorComponent } from '../ui/rich-editor';
/**
- * /p/:key/proponer — anyone, without an account, reports a bug or proposes
+ * /p/:key/propose — anyone, without an account, reports a bug or proposes
* an idea for a public project that takes them. It becomes a ticket
* labelled «propuesta» that the team looks at and others can vote for.
*/
diff --git a/libs/web/tracker/presenters/src/lib/pages/settings.page.ts b/libs/web/tracker/presenters/src/lib/pages/settings.page.ts
index 3d96c64..b2e7ee8 100644
--- a/libs/web/tracker/presenters/src/lib/pages/settings.page.ts
+++ b/libs/web/tracker/presenters/src/lib/pages/settings.page.ts
@@ -61,7 +61,7 @@ const SECTIONS: SettingsSection[] = [
hint: 'Quién ve el proyecto y si el público puede proponer cosas.',
},
{
- id: 'flujo',
+ id: 'workflow',
draft: true,
label: 'Flujo de trabajo',
hint: 'Las columnas del tablero, a cuáles se puede pasar desde cada una y cuándo algo está hecho.',
@@ -73,31 +73,31 @@ const SECTIONS: SettingsSection[] = [
hint: 'Tipos, áreas y campos propios. Lo que ya se usa no se borra: se archiva y deja de ofrecerse.',
},
{
- id: 'equipos',
+ id: 'teams',
draft: true,
label: 'Equipos',
hint: 'Grupos de miembros, para asignarles tickets y avisarles con «@equipo».',
},
{
- id: 'automatizaciones',
+ id: 'automations',
label: 'Automatizaciones',
hint: 'Reglas «cuando… si… entonces…» que hacen solas el trabajo repetido.',
needs: 'manage',
},
{
- id: 'integraciones',
+ id: 'integrations',
label: 'Integraciones',
hint: 'Commits y pull requests de Git en cada ticket, y avisos a otros sistemas.',
needs: 'manage',
},
{
- id: 'datos',
+ id: 'data',
label: 'Importar y exportar',
hint: 'Los tickets en CSV: sacarlos, o traerlos de TaskStream o de Jira.',
needs: 'manage',
},
{
- id: 'peligro',
+ id: 'danger',
label: 'Zona peligrosa',
hint: 'Archivar el proyecto o eliminarlo del todo.',
needs: 'owner',
@@ -106,13 +106,13 @@ const SECTIONS: SettingsSection[] = [
/** Which section holds each part of the draft. */
const DRAFT_SECTION: Partial
> = {
- statuses: 'flujo',
- transitions: 'flujo',
- checklistBeforeDone: 'flujo',
+ statuses: 'workflow',
+ transitions: 'workflow',
+ checklistBeforeDone: 'workflow',
issueTypes: 'tickets',
areas: 'tickets',
customFields: 'tickets',
- teams: 'equipos',
+ teams: 'teams',
};
/** "Datos y análisis" → "DATOS_Y_ANALISIS": ids of new values (they never change after saving). */
@@ -157,9 +157,9 @@ function idFrom(name: string): string {
@for (item of sections(); track item.id) {
{{ item.label }}
@@ -233,7 +233,7 @@ function idFrom(name: string): string {
proponga una idea.
} @else if (project.acceptsProposals) {
Abierto: cualquiera puede enviar un error o una idea desde
- el formularioel formulario. Llegan como tickets con la etiqueta «propuesta», y se pueden
votar.
} @else {
@@ -268,7 +268,7 @@ function idFrom(name: string): string {
>