fix(web): a poll asks its own question instead of a stock one

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 21:45:07 +02:00
parent f36c31e66d
commit 41f81cd2b0
2 changed files with 41 additions and 27 deletions

View File

@@ -301,14 +301,14 @@ visualización de la plataforma:
## 4.6 Rutas
| Ruta | Página |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `/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, 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 |
| `/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/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 («Encuesta»; con ella el botón pasa a «Publicar encuesta», y basta con dos opciones): 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 («Encuesta»; con su pregunta y de 2 a 6 opciones; el botón pasa a «Publicar encuesta» y el texto del comentario es opcional): 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/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 |

View File

@@ -1365,9 +1365,16 @@ const MAX_FILE_BYTES = 25 * 1024 * 1024;
<fieldset class="ts-poll-editor">
<legend>Encuesta · de 2 a 6 opciones</legend>
<p class="ts-poll-editor__hint">
La pregunta va en el texto de arriba. Se publica con el
comentario, al pulsar «Publicar encuesta».
Se publica con el comentario, al pulsar «Publicar
encuesta». El texto de arriba es opcional.
</p>
<pi-text-field
placeholder="¿Stripe o Redsys para los pagos?"
[value]="pollQuestion()"
(valueChange)="pollQuestion.set($event.slice(0, 200))"
>
<pi-label [isRequired]="true">Pregunta</pi-label>
</pi-text-field>
@for (option of pollOptions(); track $index) {
<div class="ts-poll-editor__row">
<pi-text-field
@@ -1446,7 +1453,7 @@ const MAX_FILE_BYTES = 25 * 1024 * 1024;
[text]="pollOn() ? 'Publicar encuesta' : 'Comentar'"
[disabled]="
pollOn()
? pollReady() < 2
? !pollQuestion().trim() || pollReady() < 2
: !commentBody().trim() && !commentFiles().length
"
(execute)="addComment()"
@@ -2583,11 +2590,12 @@ export class IssuePage {
protected addComment(): void {
const files = this.commentFiles();
// Only files (a recording, say) need no words: their names say it.
const text = this.commentBody().trim();
const question = this.pollOn() ? this.pollQuestion().trim() : '';
// A poll's question heads the comment; the text, if any, follows.
const body =
this.commentBody().trim() ||
// A poll alone asks its own question.
(this.pollOn() ? '📊 ¿Qué opción preferís?' : '') ||
(question ? `**${question}**${text ? `\n\n${text}` : ''}` : text) ||
// Only files (a recording, say) need no words: their names say it.
(files.length === 1
? `📎 ${files[0].name}`
: files.length
@@ -2597,6 +2605,10 @@ export class IssuePage {
const options = this.pollOptions()
.map((o) => o.trim())
.filter(Boolean);
if (this.pollOn() && !question) {
this.toast.error('Escribe la pregunta de la encuesta');
return;
}
if (this.pollOn() && options.length < 2) {
this.toast.error('Una encuesta necesita al menos dos opciones');
return;
@@ -2609,6 +2621,7 @@ export class IssuePage {
/** A quick poll in the new comment: its options while it is written. */
protected readonly pollOn = signal(false);
protected readonly pollQuestion = signal('');
protected readonly pollOptions = signal<string[]>(['', '']);
/** Options written so far: two make a poll ready to publish. */
protected readonly pollReady = computed(
@@ -2616,6 +2629,7 @@ export class IssuePage {
);
protected startPoll(): void {
this.pollQuestion.set('');
this.pollOptions.set(['', '']);
this.pollOn.set(true);
}