feat(jira): indicator per-card + import view desde Jira board 33
Backend:
- migration 018: cards.jira_last_status / sync_at / error (estado persistido del ultimo
sync para render UI sin polling Jira).
- Dispatcher: sync.Map inflight para 'yellow' realtime + persistencia de exito/fallo
en cards tras cada dispatch attempt.
- GET /api/cards/{id}/jira-sync: devuelve {jira_key, last_status, last_sync_at,
last_error, inflight, issue_url} para el tooltip del indicador.
- GET /api/jira/issues: lista issues del board 33 con flag already_imported +
mapped_column_id (reverse status_map). Filtros include_imported, limit.
- POST /api/jira/import: multi-key. Cada issue -> CreateCard + setCardJiraKey +
seed jira_last_status. Cae en columna mapeada por status, o en fallback_column_id.
ADF de description extraido a texto plano.
Frontend:
- JiraSyncIndicator: dot gris/amarillo/verde/rojo bajo IconDotsVertical de cada card.
Mantine HoverCard con jira_key, status, last_sync, last_error, link 'Abrir en Jira'.
Poll cada 10s, refresh-tick opcional.
- KanbanCard: agrupa menu + indicator en Stack vertical (indicator debajo de los 3 dots).
- ImportJiraModal: modal admin con tabla de issues. Checkbox por fila, filtro por texto,
toggle 'mostrar ya importadas', Select de columna fallback. Tras import recarga board.
- App.tsx: nueva entrada de menu 'Importar de Jira' (admin) y ImportJiraModal mounted.
Backend tests siguen verdes (test mock cubre transitions endpoints).
Frontend pnpm build OK.
This commit is contained in:
@@ -57,6 +57,7 @@ import {
|
||||
IconLogout,
|
||||
IconPlug,
|
||||
IconKey,
|
||||
IconBrandJira,
|
||||
IconMenu2,
|
||||
IconMessageChatbot,
|
||||
IconMoodSmile,
|
||||
@@ -86,6 +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 { useEventStream } from "./hooks/useEventStream";
|
||||
import type { Board, Card, CardColor, Column, ColumnLocation, Notification, User } from "./types";
|
||||
|
||||
@@ -364,6 +366,7 @@ export function App() {
|
||||
|
||||
const [modulesOpen, setModulesOpen] = useState(false);
|
||||
const [mcpTokensOpen, setMcpTokensOpen] = useState(false);
|
||||
const [jiraImportOpen, setJiraImportOpen] = useState(false);
|
||||
|
||||
const reloadNotifs = useCallback(async () => {
|
||||
try {
|
||||
@@ -1292,6 +1295,14 @@ export function App() {
|
||||
Modulos
|
||||
</Menu.Item>
|
||||
)}
|
||||
{auth.user.is_admin && (
|
||||
<Menu.Item
|
||||
leftSection={<IconBrandJira size={14} />}
|
||||
onClick={() => setJiraImportOpen(true)}
|
||||
>
|
||||
Importar de Jira
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Item
|
||||
leftSection={<IconKey size={14} />}
|
||||
onClick={() => setMcpTokensOpen(true)}
|
||||
@@ -1311,6 +1322,14 @@ export function App() {
|
||||
{auth.user?.is_admin && (
|
||||
<ModulesModal opened={modulesOpen} onClose={() => setModulesOpen(false)} />
|
||||
)}
|
||||
{auth.user?.is_admin && board && (
|
||||
<ImportJiraModal
|
||||
opened={jiraImportOpen}
|
||||
onClose={() => setJiraImportOpen(false)}
|
||||
columns={board.columns}
|
||||
onImported={() => reload()}
|
||||
/>
|
||||
)}
|
||||
<MCPTokensModal opened={mcpTokensOpen} onClose={() => setMcpTokensOpen(false)} />
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
Reference in New Issue
Block a user