fix(web): the ticket's header fits, with «Más» for the less frequent actions
The reminder and what is new are pi-notes; the poll editor's buttons align. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -301,14 +301,14 @@ visualización de la plataforma:
|
|||||||
## 4.6 Rutas
|
## 4.6 Rutas
|
||||||
|
|
||||||
| Ruta | Página |
|
| Ruta | Página |
|
||||||
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `/login` | Inicio de sesión (solo sin sesión) |
|
| `/login` | Inicio de sesión (solo sin sesión) |
|
||||||
| `/auth/callback` | Retorno de Pireon (PKCE) |
|
| `/auth/callback` | Retorno de Pireon (PKCE) |
|
||||||
| `/` | Inicio: saludo según la hora con el resumen del día, «Ir a…» (`WEB-12`), primeros pasos, mis proyectos con su actividad (lo primero, para llegar antes), «Mi día» (`ts-my-day`: los tickets que elijo para hoy, tachados al terminar, mis abiertos para añadir y lo que quedó del último día), mis fijados (`ts-pinned`, una franja que se quita con ×; solo si hay), 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» |
|
| `/` | Inicio: saludo según la hora con el resumen del día, «Ir a…» (`WEB-12`), primeros pasos, mis proyectos con su actividad (lo primero, para llegar antes), «Mi día» (`ts-my-day`: los tickets que elijo para hoy, tachados al terminar, mis abiertos para añadir y lo que quedó del último día), mis fijados (`ts-pinned`, una franja que se quita con ×; solo si hay), 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» |
|
||||||
| `/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. Cada proyecto lleva su semáforo de salud (`portfolioHealth`), con las causas al pasar por encima |
|
| `/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. Cada proyecto lleva su semáforo de salud (`portfolioHealth`), con las causas al pasar por encima |
|
||||||
| `/explore` | Proyectos públicos (sin sesión) |
|
| `/explore` | Proyectos públicos (sin sesión) |
|
||||||
| `/p/:key/board` | Tablero kanban (`?release=` y `?assignee=` preseleccionan los filtros). Quien puede contribuir cambia prioridad, estimación y persona desde la propia tarjeta (selectores nativos sobre sus distintivos; el título es el enlace). Mover una tarjeta o cambiar su persona ofrece «Deshacer» unos segundos (o Ctrl/⌘ Z): la orden contraria, que el flujo puede rechazar. Con el teclado: Tab entra en las tarjetas, las flechas se mueven entre ellas, Shift+←/→ pasa la tarjeta de columna (mismas reglas que arrastrar), `p`/`e`/`a` abren su prioridad, estimación o persona y Enter la abre |
|
| `/p/:key/board` | Tablero kanban (`?release=` y `?assignee=` preseleccionan los filtros). Quien puede contribuir cambia prioridad, estimación y persona desde la propia tarjeta (selectores nativos sobre sus distintivos; el título es el enlace). Mover una tarjeta o cambiar su persona ofrece «Deshacer» unos segundos (o Ctrl/⌘ Z): la orden contraria, que el flujo puede rechazar. Con el teclado: Tab entra en las tarjetas, las flechas se mueven entre ellas, Shift+←/→ pasa la tarjeta de columna (mismas reglas que arrastrar), `p`/`e`/`a` abren su prioridad, estimación o persona y Enter la abre |
|
||||||
| `/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). «Fijar» lo deja a mano en el inicio y en la paleta de búsqueda. Arriba, sus decisiones (comentarios marcados con la bandera por quien puede cambiar el ticket); `?comment=<id>` lleva a un comentario y lo señala. Cambiar el estado o la persona y mandarlo a la papelera (sin confirmación) ofrecen «Deshacer». Cada conversación con respuestas se resuelve («✓ Resolver», con una conclusión opcional que queda como decisión) y se pliega en una línea; la cabecera cuenta las abiertas y terminar el ticket con alguna pregunta antes. Se ve quién está escribiendo en cada conversación («Ana está escribiendo…», `PresenceFacade.typists`), y en las respuestas Enter envía (Shift+Enter, otra línea). Si tiene subtareas, «Seguir todo» avisa de ella y de todo lo que cuelga, también lo que se añada (también desde Épicas). «Recordármelo» pone un aviso solo para mí (dentro de 1 hora, esta tarde, mañana o el lunes a las 9, en una semana o cuando diga), con nota opcional. «☀ Mi día» lo añade a los de hoy. «Pedir revisión» a alguien del equipo: lo ve en «Mi trabajo» («Te piden revisión») y responde «Todo bien» o «Pedir cambios» desde el recuadro Revisiones. Un comentario puede llevar una encuesta («📊 Añadir una encuesta»): se vota pulsando una opción y, al cerrarla, la ganadora queda como decisión. «🎥 Grabar pantalla» (con la voz, si se da permiso al micrófono) y «🎙 Grabar audio» graban en el navegador hasta 3 minutos y lo adjuntan al comentario, que puede ir sin texto; los vídeos y audios adjuntos se reproducen ahí mismo. Al abrirlo, lo que otros han hecho desde tu última visita: un aviso arriba («Desde tu última visita, hace 2 días: 3 comentarios y 1 cambio de Ana y Bob», con «Ver lo nuevo»), los comentarios nuevos marcados («Nuevo», también en una conversación plegada) y los cambios del historial resaltados. Cada comentario se puede «Convertir en ticket» (subtarea de este o enlazado, con el título sacado de su primera línea): el comentario enlaza al ticket nuevo («→ Convertido en WEB-45») |
|
| `/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). A la vista, «Votar», «Seguir» y «Editar»; el resto (pedir revisión, mi día, recordármelo, seguir todo, fijar y duplicar) está en el menú «Más» (menú del CDK: se recorre con las flechas y Escape lo cierra). «Fijar» lo deja a mano en el inicio y en la paleta de búsqueda. Arriba, sus decisiones (comentarios marcados con la bandera por quien puede cambiar el ticket); `?comment=<id>` lleva a un comentario y lo señala. Cambiar el estado o la persona y mandarlo a la papelera (sin confirmación) ofrecen «Deshacer». Cada conversación con respuestas se resuelve («✓ Resolver», con una conclusión opcional que queda como decisión) y se pliega en una línea; la cabecera cuenta las abiertas y terminar el ticket con alguna pregunta antes. Se ve quién está escribiendo en cada conversación («Ana está escribiendo…», `PresenceFacade.typists`), y en las respuestas Enter envía (Shift+Enter, otra línea). Si tiene subtareas, «Seguir todo» avisa de ella y de todo lo que cuelga, también lo que se añada (también desde Épicas). «Recordármelo» pone un aviso solo para mí (dentro de 1 hora, esta tarde, mañana o el lunes a las 9, en una semana o cuando diga), con nota opcional. «☀ Mi día» lo añade a los de hoy. «Pedir revisión» a alguien del equipo: lo ve en «Mi trabajo» («Te piden revisión») y responde «Todo bien» o «Pedir cambios» desde el recuadro Revisiones. Un comentario puede llevar una encuesta («📊 Añadir una encuesta»): se vota pulsando una opción y, al cerrarla, la ganadora queda como decisión. «🎥 Grabar pantalla» (con la voz, si se da permiso al micrófono) y «🎙 Grabar audio» graban en el navegador hasta 3 minutos y lo adjuntan al comentario, que puede ir sin texto; los vídeos y audios adjuntos se reproducen ahí mismo. Al abrirlo, lo que otros han hecho desde tu última visita: un aviso arriba («Desde tu última visita, hace 2 días: 3 comentarios y 1 cambio de Ana y Bob», con «Ver lo nuevo»), los comentarios nuevos marcados («Nuevo», también en una conversación plegada) y los cambios del historial resaltados. Cada comentario se puede «Convertir en ticket» (subtarea de este o enlazado, con el título sacado de su primera línea): el comentario enlaza al ticket nuevo («→ Convertido en WEB-45») |
|
||||||
| `/p/:key/releases` | Releases. «Página pública» (quien gestiona): un enlace secreto con las notas para clientes, que se copia, renueva o retira |
|
| `/p/:key/releases` | Releases. «Página pública» (quien gestiona): un enlace secreto con las notas para clientes, que se copia, renueva o retira |
|
||||||
| `/p/:key/report/:kind/:id` | Informe para imprimir de un sprint (`sprint`) o una release (`release`): datos, totales, notas y tickets por etapa. «Imprimir o guardar en PDF» abre el diálogo del navegador; al imprimir se quitan la cabecera, las pestañas y los fondos (`styles.scss`) |
|
| `/p/:key/report/:kind/:id` | Informe para imprimir de un sprint (`sprint`) o una release (`release`): datos, totales, notas y tickets por etapa. «Imprimir o guardar en PDF» abre el diálogo del navegador; al imprimir se quitan la cabecera, las pestañas y los fondos (`styles.scss`) |
|
||||||
| `/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. Los bloqueos con tickets de otros proyectos (en los dos sentidos, `externalDependencies`) salen en «De otros proyectos» con barra discontinua, sus flechas y el mismo aviso si las fechas no encajan; los que no tienen fechas o no se pueden leer se nombran debajo |
|
| `/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. Los bloqueos con tickets de otros proyectos (en los dos sentidos, `externalDependencies`) salen en «De otros proyectos» con barra discontinua, sus flechas y el mismo aviso si las fechas no encajan; los que no tienen fechas o no se pueden leer se nombran debajo |
|
||||||
|
|||||||
@@ -69,12 +69,101 @@
|
|||||||
// Follow and edit, together, level with the title's first line.
|
// Follow and edit, together, level with the title's first line.
|
||||||
.ts-issue__actions {
|
.ts-issue__actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-wrap: wrap;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: 0.25rem;
|
margin-top: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* «Más»: the ticket's less frequent actions, in a menu. */
|
||||||
|
/* Laid out as pi-icon-button lays out its own (its styles are scoped). */
|
||||||
|
.ts-more {
|
||||||
|
display: flex !important;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
display: flex;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ts-menu {
|
||||||
|
display: grid;
|
||||||
|
width: min(22rem, calc(100vw - 2rem));
|
||||||
|
padding: 0.375rem;
|
||||||
|
border: 1px solid var(--ts-line);
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
background: var(--ts-surface);
|
||||||
|
box-shadow: 0 12px 32px rgb(0 0 0 / 14%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @pi/ui styles every <button> globally, hence the !important. */
|
||||||
|
.ts-menu__item {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns: 1.25rem minmax(0, 1fr) auto;
|
||||||
|
gap: 0.75rem;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0 !important;
|
||||||
|
height: auto !important;
|
||||||
|
padding: 0.5rem 0.625rem !important;
|
||||||
|
border: 0 !important;
|
||||||
|
border-radius: 0.5rem !important;
|
||||||
|
background: transparent !important;
|
||||||
|
color: var(--ts-ink) !important;
|
||||||
|
font: inherit;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&.cdk-menu-item-focused {
|
||||||
|
background: var(--ts-sunken) !important;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 2px solid var(--ts-brand);
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pi-icon {
|
||||||
|
color: var(--ts-ink-soft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ts-menu__text {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
small {
|
||||||
|
color: var(--ts-muted);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ts-menu__check {
|
||||||
|
color: var(--ts-brand);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A note above the ticket (my reminder, what is new). */
|
||||||
|
.ts-banner {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
row-gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ts-banner__actions {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
// Workflow: one button per allowed destination, in that status' colour.
|
// Workflow: one button per allowed destination, in that status' colour.
|
||||||
.ts-transitions {
|
.ts-transitions {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -373,26 +462,6 @@ a.ts-key {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* What others did since my last visit, above the ticket. */
|
|
||||||
.ts-news {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.25rem 0.625rem;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.5rem 0.875rem;
|
|
||||||
border-radius: 0.625rem;
|
|
||||||
background: color-mix(in srgb, var(--ts-brand) 9%, transparent);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ts-news__dot {
|
|
||||||
width: 0.5rem;
|
|
||||||
height: 0.5rem;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--ts-brand);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ts-comment.is-pointed {
|
.ts-comment.is-pointed {
|
||||||
animation: ts-pointed 2.4s ease-out;
|
animation: ts-pointed 2.4s ease-out;
|
||||||
}
|
}
|
||||||
@@ -827,25 +896,6 @@ a.ts-key {
|
|||||||
background: var(--ts-c-green, #2e9e6a);
|
background: var(--ts-c-green, #2e9e6a);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* My own reminder on the ticket. */
|
|
||||||
.ts-reminder {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.25rem 0.625rem;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.5rem 0.875rem;
|
|
||||||
border-radius: 0.625rem;
|
|
||||||
background: color-mix(in srgb, var(--ts-c-amber) 14%, transparent);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ts-reminder__actions {
|
|
||||||
display: inline-flex;
|
|
||||||
gap: 0.25rem;
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ts-reminder__choices {
|
.ts-reminder__choices {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ import { toObservable, toSignal } from '@angular/core/rxjs-interop';
|
|||||||
import { form, FormField, required } from '@angular/forms/signals';
|
import { form, FormField, required } from '@angular/forms/signals';
|
||||||
import { catchError, distinctUntilChanged, filter, of, switchMap } from 'rxjs';
|
import { catchError, distinctUntilChanged, filter, of, switchMap } from 'rxjs';
|
||||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||||
|
import {
|
||||||
|
CdkMenu,
|
||||||
|
CdkMenuItem,
|
||||||
|
CdkMenuItemCheckbox,
|
||||||
|
CdkMenuTrigger,
|
||||||
|
} from '@angular/cdk/menu';
|
||||||
|
import { ConnectedPosition } from '@angular/cdk/overlay';
|
||||||
|
import { MatIcon } from '@angular/material/icon';
|
||||||
import {
|
import {
|
||||||
PiActionButtonComponent,
|
PiActionButtonComponent,
|
||||||
PiCheckboxComponent,
|
PiCheckboxComponent,
|
||||||
@@ -25,6 +33,7 @@ import {
|
|||||||
PiIconComponent,
|
PiIconComponent,
|
||||||
PiLabelDirective,
|
PiLabelDirective,
|
||||||
PiNoteComponent,
|
PiNoteComponent,
|
||||||
|
PiNoteActionContentDirective,
|
||||||
PiSegmentedControlComponent,
|
PiSegmentedControlComponent,
|
||||||
PiProgressBarComponent,
|
PiProgressBarComponent,
|
||||||
PiSearchableSelectFieldComponent,
|
PiSearchableSelectFieldComponent,
|
||||||
@@ -108,6 +117,11 @@ const MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|||||||
RichEditorComponent,
|
RichEditorComponent,
|
||||||
NgTemplateOutlet,
|
NgTemplateOutlet,
|
||||||
RouterLink,
|
RouterLink,
|
||||||
|
CdkMenu,
|
||||||
|
CdkMenuItem,
|
||||||
|
CdkMenuItemCheckbox,
|
||||||
|
CdkMenuTrigger,
|
||||||
|
MatIcon,
|
||||||
FormField,
|
FormField,
|
||||||
AttachmentItem,
|
AttachmentItem,
|
||||||
MediaRecorderComponent,
|
MediaRecorderComponent,
|
||||||
@@ -119,6 +133,7 @@ const MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|||||||
PiIconButtonComponent,
|
PiIconButtonComponent,
|
||||||
PiLabelDirective,
|
PiLabelDirective,
|
||||||
PiNoteComponent,
|
PiNoteComponent,
|
||||||
|
PiNoteActionContentDirective,
|
||||||
PiSegmentedControlComponent,
|
PiSegmentedControlComponent,
|
||||||
PiProgressBarComponent,
|
PiProgressBarComponent,
|
||||||
PiSearchableSelectFieldComponent,
|
PiSearchableSelectFieldComponent,
|
||||||
@@ -223,79 +238,6 @@ const MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|||||||
(execute)="facade.setWatching(!facade.watching())"
|
(execute)="facade.setWatching(!facade.watching())"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@if (project.canContribute()) {
|
|
||||||
<pi-icon-button
|
|
||||||
icon="eye"
|
|
||||||
color="grey"
|
|
||||||
appearance="outline"
|
|
||||||
text="Pedir revisión"
|
|
||||||
title="Pedir a alguien del equipo que lo revise"
|
|
||||||
(execute)="openReviewRequest()"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
@if (project.myRole() && inMyDay() !== null) {
|
|
||||||
<pi-icon-button
|
|
||||||
icon="sun"
|
|
||||||
color="grey"
|
|
||||||
appearance="outline"
|
|
||||||
[text]="inMyDay() ? 'En mi día' : 'Mi día'"
|
|
||||||
[attr.aria-pressed]="inMyDay()"
|
|
||||||
[attr.title]="
|
|
||||||
inMyDay()
|
|
||||||
? 'Quitar de lo que quiero sacar hoy'
|
|
||||||
: 'Añadir a lo que quiero sacar hoy (en el inicio)'
|
|
||||||
"
|
|
||||||
(execute)="toggleMyDay(issue)"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
@if (project.myRole()) {
|
|
||||||
<pi-icon-button
|
|
||||||
icon="bell"
|
|
||||||
color="grey"
|
|
||||||
appearance="outline"
|
|
||||||
[text]="issue.myReminder ? 'Recordatorio' : 'Recordármelo'"
|
|
||||||
[attr.aria-pressed]="!!issue.myReminder"
|
|
||||||
title="Avisarme de este ticket cuando yo diga"
|
|
||||||
(execute)="openReminder()"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
@if (
|
|
||||||
project.myRole() &&
|
|
||||||
(facade.children().length || issue.followingTree)
|
|
||||||
) {
|
|
||||||
<pi-icon-button
|
|
||||||
icon="kanban"
|
|
||||||
color="grey"
|
|
||||||
appearance="outline"
|
|
||||||
[text]="
|
|
||||||
issue.followingTree
|
|
||||||
? 'Siguiendo todo · ' + facade.children().length
|
|
||||||
: 'Seguir todo'
|
|
||||||
"
|
|
||||||
[attr.aria-pressed]="issue.followingTree"
|
|
||||||
[attr.title]="
|
|
||||||
issue.followingTree
|
|
||||||
? 'Dejar de seguir lo que cuelga de este ticket'
|
|
||||||
: 'Avisarme de este ticket y de todo lo que cuelga de él, también lo que se añada'
|
|
||||||
"
|
|
||||||
(execute)="facade.setFollowingTree(!issue.followingTree)"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
@if (project.myRole()) {
|
|
||||||
<pi-icon-button
|
|
||||||
icon="pin"
|
|
||||||
color="grey"
|
|
||||||
appearance="outline"
|
|
||||||
[text]="issue.pinned ? 'Fijado' : 'Fijar'"
|
|
||||||
[attr.aria-pressed]="issue.pinned"
|
|
||||||
[attr.title]="
|
|
||||||
issue.pinned
|
|
||||||
? 'Quitar de mis fijados'
|
|
||||||
: 'Tenerlo a mano en el inicio y al buscar'
|
|
||||||
"
|
|
||||||
(execute)="facade.setPinned(!issue.pinned)"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
@if (project.canContribute()) {
|
@if (project.canContribute()) {
|
||||||
<pi-icon-button
|
<pi-icon-button
|
||||||
icon="edit"
|
icon="edit"
|
||||||
@@ -305,64 +247,210 @@ const MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|||||||
title="Editar (tecla E)"
|
title="Editar (tecla E)"
|
||||||
(execute)="openEdit()"
|
(execute)="openEdit()"
|
||||||
/>
|
/>
|
||||||
<pi-icon-button
|
|
||||||
icon="copy"
|
|
||||||
color="grey"
|
|
||||||
appearance="outline"
|
|
||||||
text="Duplicar"
|
|
||||||
title="Crear otro ticket con lo mismo (sin estado, persona ni comentarios)"
|
|
||||||
(execute)="duplicate()"
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
|
@if (project.myRole()) {
|
||||||
|
<!-- @pi/ui's look on a native button: the menu needs it. -->
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="ts-more has-text pi-button pi-button__appearance--outline pi-button__icon pi-button__medium pi-color__grey pi-color__mode--dark"
|
||||||
|
[cdkMenuTriggerFor]="moreMenu"
|
||||||
|
[cdkMenuPosition]="morePositions"
|
||||||
|
>
|
||||||
|
<span>Más</span>
|
||||||
|
<mat-icon svgIcon="chevron-down" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
<ng-template #moreMenu>
|
||||||
|
<div class="ts-menu" cdkMenu>
|
||||||
|
@if (project.canContribute()) {
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="ts-menu__item"
|
||||||
|
cdkMenuItem
|
||||||
|
(cdkMenuItemTriggered)="openReviewRequest()"
|
||||||
|
>
|
||||||
|
<pi-icon icon="eye" size="small" />
|
||||||
|
<span class="ts-menu__text"
|
||||||
|
>Pedir revisión<small
|
||||||
|
>A alguien del equipo, que lo verá en «Mi
|
||||||
|
trabajo»</small
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
@if (inMyDay() !== null) {
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="ts-menu__item"
|
||||||
|
cdkMenuItemCheckbox
|
||||||
|
[cdkMenuItemChecked]="!!inMyDay()"
|
||||||
|
(cdkMenuItemTriggered)="toggleMyDay(issue)"
|
||||||
|
>
|
||||||
|
<pi-icon icon="sun" size="small" />
|
||||||
|
<span class="ts-menu__text"
|
||||||
|
>{{ inMyDay() ? 'En mi día' : 'Añadir a mi día'
|
||||||
|
}}<small
|
||||||
|
>Lo que quiero sacar hoy, en el inicio</small
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
@if (inMyDay()) {
|
||||||
|
<span class="ts-menu__check" aria-hidden="true"
|
||||||
|
>✓</span
|
||||||
|
>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="ts-menu__item"
|
||||||
|
cdkMenuItem
|
||||||
|
(cdkMenuItemTriggered)="openReminder()"
|
||||||
|
>
|
||||||
|
<pi-icon icon="bell" size="small" />
|
||||||
|
<span class="ts-menu__text"
|
||||||
|
>{{
|
||||||
|
issue.myReminder
|
||||||
|
? 'Cambiar el recordatorio'
|
||||||
|
: 'Recordármelo'
|
||||||
|
}}<small
|
||||||
|
>Un aviso solo para mí, cuando yo diga</small
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
@if (facade.children().length || issue.followingTree) {
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="ts-menu__item"
|
||||||
|
cdkMenuItemCheckbox
|
||||||
|
[cdkMenuItemChecked]="issue.followingTree"
|
||||||
|
(cdkMenuItemTriggered)="
|
||||||
|
facade.setFollowingTree(!issue.followingTree)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<pi-icon icon="kanban" size="small" />
|
||||||
|
<span class="ts-menu__text"
|
||||||
|
>{{
|
||||||
|
issue.followingTree
|
||||||
|
? 'Siguiendo todo · ' + facade.children().length
|
||||||
|
: 'Seguir todo'
|
||||||
|
}}<small
|
||||||
|
>Este ticket y lo que cuelga de él, también lo
|
||||||
|
nuevo</small
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
@if (issue.followingTree) {
|
||||||
|
<span class="ts-menu__check" aria-hidden="true"
|
||||||
|
>✓</span
|
||||||
|
>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="ts-menu__item"
|
||||||
|
cdkMenuItemCheckbox
|
||||||
|
[cdkMenuItemChecked]="issue.pinned"
|
||||||
|
(cdkMenuItemTriggered)="facade.setPinned(!issue.pinned)"
|
||||||
|
>
|
||||||
|
<pi-icon icon="pin" size="small" />
|
||||||
|
<span class="ts-menu__text"
|
||||||
|
>{{ issue.pinned ? 'Fijado' : 'Fijar'
|
||||||
|
}}<small>A mano en el inicio y al buscar</small></span
|
||||||
|
>
|
||||||
|
@if (issue.pinned) {
|
||||||
|
<span class="ts-menu__check" aria-hidden="true">✓</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
@if (project.canContribute()) {
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="ts-menu__item"
|
||||||
|
cdkMenuItem
|
||||||
|
(cdkMenuItemTriggered)="duplicate()"
|
||||||
|
>
|
||||||
|
<pi-icon icon="copy" size="small" />
|
||||||
|
<span class="ts-menu__text"
|
||||||
|
>Duplicar<small
|
||||||
|
>Otro ticket con lo mismo, sin estado ni
|
||||||
|
comentarios</small
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@if (issue.myReminder; as reminder) {
|
@if (issue.myReminder; as reminder) {
|
||||||
<p class="ts-reminder" role="status">
|
<pi-note
|
||||||
<span aria-hidden="true">⏰</span>
|
class="ts-banner"
|
||||||
Te lo recordaré {{ when(reminder.at) }}
|
gap="xs"
|
||||||
@if (reminder.note) {
|
status="info"
|
||||||
· «{{ reminder.note }}»
|
appearance="fill"
|
||||||
}
|
type="status"
|
||||||
<span class="ts-reminder__actions">
|
justify="space-between"
|
||||||
<pi-action-button
|
alignment="center"
|
||||||
size="x-small"
|
[title]="'Te lo recordaré ' + when(reminder.at)"
|
||||||
|
[text]="reminder.note ? '«' + reminder.note + '»' : ''"
|
||||||
|
>
|
||||||
|
<pi-note-action-content>
|
||||||
|
<span class="ts-banner__actions">
|
||||||
|
<pi-icon-button
|
||||||
|
icon="edit"
|
||||||
color="grey"
|
color="grey"
|
||||||
appearance="outline"
|
appearance="outline"
|
||||||
|
size="small"
|
||||||
text="Cambiar"
|
text="Cambiar"
|
||||||
(execute)="openReminder()"
|
(execute)="openReminder()"
|
||||||
/>
|
/>
|
||||||
<pi-action-button
|
<pi-icon-button
|
||||||
size="x-small"
|
icon="trash"
|
||||||
color="grey"
|
color="grey"
|
||||||
appearance="ghost"
|
appearance="outline"
|
||||||
|
size="small"
|
||||||
text="Quitar"
|
text="Quitar"
|
||||||
(execute)="facade.setReminder(null)"
|
(execute)="facade.setReminder(null)"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</pi-note-action-content>
|
||||||
|
</pi-note>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (news(); as news) {
|
@if (news(); as news) {
|
||||||
<p class="ts-news" role="status">
|
<pi-note
|
||||||
<span class="ts-news__dot" aria-hidden="true"></span>
|
class="ts-banner"
|
||||||
<span
|
gap="xs"
|
||||||
>Desde tu última visita, {{ ago(news.since) }}:
|
status="info"
|
||||||
<strong>{{ news.what }}</strong> de {{ news.who }}.</span
|
appearance="fill"
|
||||||
|
type="status"
|
||||||
|
justify="space-between"
|
||||||
|
alignment="center"
|
||||||
|
[title]="
|
||||||
|
'Desde tu última visita (' +
|
||||||
|
ago(news.since) +
|
||||||
|
'): ' +
|
||||||
|
news.what +
|
||||||
|
' de ' +
|
||||||
|
news.who
|
||||||
|
"
|
||||||
|
text=""
|
||||||
>
|
>
|
||||||
@if (news.firstComment; as first) {
|
@if (news.firstComment; as first) {
|
||||||
<span class="ts-reminder__actions">
|
<pi-note-action-content>
|
||||||
|
<span class="ts-banner__actions">
|
||||||
<pi-action-button
|
<pi-action-button
|
||||||
size="x-small"
|
size="small"
|
||||||
color="grey"
|
color="grey"
|
||||||
appearance="outline"
|
appearance="outline"
|
||||||
text="Ver lo nuevo"
|
text="Ver lo nuevo"
|
||||||
(execute)="goToNews(first)"
|
(execute)="goToNews(first)"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
</pi-note-action-content>
|
||||||
}
|
}
|
||||||
</p>
|
</pi-note>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (project.canContribute() && issue.allowedTransitions.length) {
|
@if (project.canContribute() && issue.allowedTransitions.length) {
|
||||||
@@ -1311,11 +1399,11 @@ const MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|||||||
(execute)="pollOptions.set([...pollOptions(), ''])"
|
(execute)="pollOptions.set([...pollOptions(), ''])"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
<pi-action-button
|
<pi-icon-button
|
||||||
size="small"
|
icon="trash"
|
||||||
color="grey"
|
color="grey"
|
||||||
appearance="ghost"
|
appearance="outline"
|
||||||
[hasPadding]="false"
|
size="small"
|
||||||
text="Quitar la encuesta"
|
text="Quitar la encuesta"
|
||||||
(execute)="pollOn.set(false)"
|
(execute)="pollOn.set(false)"
|
||||||
/>
|
/>
|
||||||
@@ -2226,6 +2314,23 @@ export class IssuePage {
|
|||||||
protected readonly inMyDay = signal<boolean | null>(null);
|
protected readonly inMyDay = signal<boolean | null>(null);
|
||||||
/** Changes with the ticket, not with each reload of it. */
|
/** Changes with the ticket, not with each reload of it. */
|
||||||
private readonly openIssueId = computed(() => this.facade.issue()?.id);
|
private readonly openIssueId = computed(() => this.facade.issue()?.id);
|
||||||
|
/** «Más» opens under its button, aligned to its right edge. */
|
||||||
|
protected readonly morePositions: ConnectedPosition[] = [
|
||||||
|
{
|
||||||
|
originX: 'end',
|
||||||
|
originY: 'bottom',
|
||||||
|
overlayX: 'end',
|
||||||
|
overlayY: 'top',
|
||||||
|
offsetY: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
originX: 'end',
|
||||||
|
originY: 'top',
|
||||||
|
overlayX: 'end',
|
||||||
|
overlayY: 'bottom',
|
||||||
|
offsetY: -6,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
/* «Lo nuevo desde tu última visita»: what others did since I last came. */
|
/* «Lo nuevo desde tu última visita»: what others did since I last came. */
|
||||||
private readonly visit = signal<{
|
private readonly visit = signal<{
|
||||||
|
|||||||
Reference in New Issue
Block a user