import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { apiFetchAppPool } from '../api'; import type { AppPoolItem } from '../api'; interface AppPoolModalProps { open: boolean; onClose: () => void; onSelect: (keyword: string) => void; allowedApps: string; } const formatWhen = (iso: string) => { if (!iso) return '—'; const d = new Date(iso); if (Number.isNaN(d.getTime())) return '—'; return d.toLocaleString('vi-VN', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }); }; export const AppPoolModal: React.FC = ({ open, onClose, onSelect, allowedApps, }) => { const [search, setSearch] = useState(''); const [debouncedQ, setDebouncedQ] = useState(''); const [items, setItems] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const allowedSet = useMemo(() => { return new Set( allowedApps.split(',').map(s => s.trim().toLowerCase()).filter(Boolean) ); }, [allowedApps]); useEffect(() => { if (!open) return; const t = setTimeout(() => setDebouncedQ(search.trim()), 300); return () => clearTimeout(t); }, [search, open]); const loadPool = useCallback(async (q: string) => { try { setLoading(true); setError(null); const res = await apiFetchAppPool(q, 80); setItems(res.data || []); } catch (e: any) { setItems([]); setError(e?.message || 'Không tải được kho ứng dụng'); } finally { setLoading(false); } }, []); useEffect(() => { if (!open) return; loadPool(debouncedQ); const interval = setInterval(() => loadPool(debouncedQ), 20000); return () => clearInterval(interval); }, [open, debouncedQ, loadPool]); useEffect(() => { if (!open) { setSearch(''); setDebouncedQ(''); setItems([]); setError(null); } }, [open]); if (!open) return null; const handleSelect = (keyword: string) => { const kw = keyword.trim().toLowerCase(); if (!kw || allowedSet.has(kw)) return; onSelect(kw); }; return (
e.stopPropagation()}>

Kho ứng dụng

Toàn hệ thống — app bị chặn từ mọi lớp. Chọn để thêm vào whitelist lớp hiện tại.

setSearch(e.target.value)} autoFocus />
{error ? (
{error}
) : loading && items.length === 0 ? (
Đang tải...
) : items.length === 0 ? (
{debouncedQ ? `Không tìm thấy "${debouncedQ}"` : 'Chưa có app nào trong kho.'}
) : ( {items.map(app => { const added = allowedSet.has(app.keyword.toLowerCase()); return ( ); })}
Keyword Tiến trình Tiêu đề Lần chặn Gần nhất
{app.keyword} {app.processName} {app.windowTitle || '—'} {app.hitCount}× {formatWhen(app.lastSeenAt)} {added ? ( Đã có ) : ( )}
)}
Hiển thị {items.length} kết quả{debouncedQ ? ` cho "${debouncedQ}"` : ''} (tối đa 80 mỗi lần tải)
); };