chore: auto-commit (12 archivos)
- app.md - backend/handlers.go - backend/main.go - frontend/src/App.tsx - frontend/src/api.ts - frontend/vite.config.ts - backend/mcp_http.go - backend/mcp_tokens.go - backend/mcp_tokens_handlers.go - backend/migrations/016_mcp_tokens.sql - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+36
-3
@@ -56,6 +56,7 @@ import {
|
||||
IconLayoutKanban,
|
||||
IconLogout,
|
||||
IconPlug,
|
||||
IconKey,
|
||||
IconMenu2,
|
||||
IconMessageChatbot,
|
||||
IconMoodSmile,
|
||||
@@ -84,6 +85,7 @@ import { AVATAR_COLORS } from "./components/colors";
|
||||
import { colorBg, colorBorder } from "./components/colors";
|
||||
import { NotificationsBell } from "./components/NotificationsBell";
|
||||
import { ModulesModal } from "./components/ModulesModal";
|
||||
import { MCPTokensModal } from "./components/MCPTokensModal";
|
||||
import { useEventStream } from "./hooks/useEventStream";
|
||||
import type { Board, Card, CardColor, Column, ColumnLocation, Notification, User } from "./types";
|
||||
|
||||
@@ -255,6 +257,23 @@ export function App() {
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Coalesce ráfagas de board.invalidated (trailing debounce 300ms) — sin esto
|
||||
// cada mutación remota dispara un refetch /api/board completo y la memoria
|
||||
// del navegador crece sin techo.
|
||||
const reloadTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const debouncedReload = useCallback(() => {
|
||||
if (reloadTimerRef.current) clearTimeout(reloadTimerRef.current);
|
||||
reloadTimerRef.current = setTimeout(() => {
|
||||
reloadTimerRef.current = null;
|
||||
reload();
|
||||
}, 300);
|
||||
}, [reload]);
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (reloadTimerRef.current) clearTimeout(reloadTimerRef.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
reload();
|
||||
}, [reload]);
|
||||
@@ -344,6 +363,7 @@ export function App() {
|
||||
}, []);
|
||||
|
||||
const [modulesOpen, setModulesOpen] = useState(false);
|
||||
const [mcpTokensOpen, setMcpTokensOpen] = useState(false);
|
||||
|
||||
const reloadNotifs = useCallback(async () => {
|
||||
try {
|
||||
@@ -367,7 +387,7 @@ export function App() {
|
||||
useMemo(
|
||||
() => ({
|
||||
"board.invalidated": () => {
|
||||
reload();
|
||||
debouncedReload();
|
||||
},
|
||||
"notification.created": (payload: unknown) => {
|
||||
const n = payload as Notification;
|
||||
@@ -377,6 +397,7 @@ export function App() {
|
||||
const who = n.actor_name || "Alguien";
|
||||
const card = n.card_seq_num ? `#${n.card_seq_num}` : n.card_title;
|
||||
notifications.show({
|
||||
autoClose: 4000,
|
||||
color: n.kind === "mention" ? "grape" : "blue",
|
||||
title: `${who} en ${card}`,
|
||||
message: n.snippet,
|
||||
@@ -393,7 +414,7 @@ export function App() {
|
||||
setNotifUnread(0);
|
||||
},
|
||||
}),
|
||||
[reload],
|
||||
[debouncedReload],
|
||||
),
|
||||
!!auth.user,
|
||||
);
|
||||
@@ -428,16 +449,21 @@ export function App() {
|
||||
(c: Card): boolean => {
|
||||
const term = searchTerm.trim().toLowerCase();
|
||||
if (term) {
|
||||
const seqStr = c.seq_num > 0 ? String(c.seq_num) : "";
|
||||
const seqPadded = c.seq_num > 0 ? String(c.seq_num).padStart(5, "0") : "";
|
||||
const hay = [
|
||||
c.title,
|
||||
c.description,
|
||||
c.requester,
|
||||
seqStr,
|
||||
seqPadded,
|
||||
...(c.tags || []),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ")
|
||||
.toLowerCase();
|
||||
if (!hay.includes(term)) return false;
|
||||
const normalizedTerm = term.replace(/^#/, "").replace(/^0+(?=\d)/, "");
|
||||
if (!hay.includes(term) && !(normalizedTerm && hay.includes(normalizedTerm))) return false;
|
||||
}
|
||||
if (filterAssigneeId && c.assignee_id !== filterAssigneeId) return false;
|
||||
if (filterUnassigned && c.assignee_id) return false;
|
||||
@@ -1266,6 +1292,12 @@ export function App() {
|
||||
Modulos
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Item
|
||||
leftSection={<IconKey size={14} />}
|
||||
onClick={() => setMcpTokensOpen(true)}
|
||||
>
|
||||
MCP tokens
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
leftSection={<IconLogout size={14} />}
|
||||
color="red"
|
||||
@@ -1279,6 +1311,7 @@ export function App() {
|
||||
{auth.user?.is_admin && (
|
||||
<ModulesModal opened={modulesOpen} onClose={() => setModulesOpen(false)} />
|
||||
)}
|
||||
<MCPTokensModal opened={mcpTokensOpen} onClose={() => setMcpTokensOpen(false)} />
|
||||
</Group>
|
||||
</Group>
|
||||
</AppShell.Header>
|
||||
|
||||
Reference in New Issue
Block a user