feat(jira): menu 'Jira' (rename) + modal con tabs Importar/Comprobar columnas

UI:
- Menu avatar dropdown: 'Importar de Jira' -> 'Jira' (renombrado).
- ImportJiraModal.tsx eliminado. Sustituido por JiraModal.tsx con Mantine Tabs:
  * 'Importar de Jira': UI heredada del modal anterior intacta.
  * 'Comprobar columnas': nueva. Lista cards linked y muestra desincronizadas
    (kanban col vs Jira status actual). Por cada row: kanban col + expected jira
    status + jira status real. Checkbox multi-select + boton 'Sincronizar' que
    empuja Jira al status correcto (kanban gana).

Backend:
- GET /api/jira/check-columns: walk cards.jira_key != ''. Por cada uno GET
  /rest/api/3/issue/{key}?fields=status. Compara status real vs status_map.
  Devuelve {rows[], total, mismatches, in_sync, status_map, reverse_map}.
- POST /api/jira/reconcile-columns {card_ids[], direction:'kanban-wins'}:
  reusa jiraHandler.transitionToStatus para empujar cada issue al status del
  status_map de su columna kanban actual + actualiza cards.jira_last_status.
- Helper listLinkedCardsForCheck en jira_import.go.

Direction='kanban-wins' default. Reverse direction (jira-wins) no soportado
por ahora: mover cards desde el server tiene efectos colaterales (eventos,
notificaciones, timers) que no quiero disparar masivos sin pensar.
This commit is contained in:
egutierrez
2026-05-29 15:18:59 +02:00
parent 9b0b6e516c
commit d4558667f6
8 changed files with 871 additions and 436 deletions
+4 -4
View File
@@ -87,7 +87,7 @@ import { colorBg, colorBorder } from "./components/colors";
import { NotificationsBell } from "./components/NotificationsBell";
import { ModulesModal } from "./components/ModulesModal";
import { MCPTokensModal } from "./components/MCPTokensModal";
import { ImportJiraModal } from "./components/ImportJiraModal";
import { JiraModal } from "./components/JiraModal";
import { useEventStream } from "./hooks/useEventStream";
import type { Board, Card, CardColor, Column, ColumnLocation, Notification, User } from "./types";
@@ -1304,7 +1304,7 @@ export function App() {
leftSection={<IconBrandJira size={14} />}
onClick={() => setJiraImportOpen(true)}
>
Importar de Jira
Jira
</Menu.Item>
)}
<Menu.Item
@@ -1327,11 +1327,11 @@ export function App() {
<ModulesModal opened={modulesOpen} onClose={() => setModulesOpen(false)} />
)}
{auth.user?.is_admin && board && (
<ImportJiraModal
<JiraModal
opened={jiraImportOpen}
onClose={() => setJiraImportOpen(false)}
columns={board.columns}
onImported={() => reload()}
onMutated={() => reload()}
/>
)}
<MCPTokensModal opened={mcpTokensOpen} onClose={() => setMcpTokensOpen(false)} />