@samanhappy/mcphub 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/keys.js +2 -7
- package/dist/cli/commands/keys.js.map +1 -1
- package/dist/controllers/bearerKeyController.js +90 -79
- package/dist/controllers/bearerKeyController.js.map +1 -1
- package/dist/controllers/oauthServerController.js +9 -4
- package/dist/controllers/oauthServerController.js.map +1 -1
- package/dist/controllers/serverController.js +62 -4
- package/dist/controllers/serverController.js.map +1 -1
- package/dist/controllers/userController.js +6 -4
- package/dist/controllers/userController.js.map +1 -1
- package/dist/dao/BearerKeyDao.js +21 -1
- package/dist/dao/BearerKeyDao.js.map +1 -1
- package/dist/dao/BearerKeyDaoDbImpl.js +13 -0
- package/dist/dao/BearerKeyDaoDbImpl.js.map +1 -1
- package/dist/dao/UserDao.js +11 -1
- package/dist/dao/UserDao.js.map +1 -1
- package/dist/dao/UserDaoDbImpl.js +39 -30
- package/dist/dao/UserDaoDbImpl.js.map +1 -1
- package/dist/db/entities/BearerKey.js +8 -0
- package/dist/db/entities/BearerKey.js.map +1 -1
- package/dist/db/entities/User.js +8 -0
- package/dist/db/entities/User.js.map +1 -1
- package/dist/db/repositories/BearerKeyRepository.js +9 -0
- package/dist/db/repositories/BearerKeyRepository.js.map +1 -1
- package/dist/db/repositories/SystemConfigRepository.js +2 -1
- package/dist/db/repositories/SystemConfigRepository.js.map +1 -1
- package/dist/db/repositories/UserRepository.js +6 -0
- package/dist/db/repositories/UserRepository.js.map +1 -1
- package/dist/middlewares/auth.js +2 -2
- package/dist/middlewares/auth.js.map +1 -1
- package/dist/middlewares/userContext.js +5 -0
- package/dist/middlewares/userContext.js.map +1 -1
- package/dist/models/User.js +25 -1
- package/dist/models/User.js.map +1 -1
- package/dist/routes/index.js +7 -7
- package/dist/routes/index.js.map +1 -1
- package/dist/services/betterAuthConfig.js +2 -0
- package/dist/services/betterAuthConfig.js.map +1 -1
- package/dist/services/betterAuthSession.js +78 -13
- package/dist/services/betterAuthSession.js.map +1 -1
- package/dist/services/mcpService.js +16 -0
- package/dist/services/mcpService.js.map +1 -1
- package/dist/services/oauthServerService.js +9 -4
- package/dist/services/oauthServerService.js.map +1 -1
- package/dist/services/smartRoutingService.js +2 -0
- package/dist/services/smartRoutingService.js.map +1 -1
- package/dist/services/sseService.js +38 -3
- package/dist/services/sseService.js.map +1 -1
- package/dist/services/userService.js +15 -4
- package/dist/services/userService.js.map +1 -1
- package/dist/utils/migration.js +6 -0
- package/dist/utils/migration.js.map +1 -1
- package/frontend/dist/assets/{ActivityPage-CLXufyzU.js → ActivityPage-CjUx2EbK.js} +2 -2
- package/frontend/dist/assets/{ActivityPage-CLXufyzU.js.map → ActivityPage-CjUx2EbK.js.map} +1 -1
- package/frontend/dist/assets/{Dashboard-YMaaIlIZ.js → Dashboard-Iq4lJkw-.js} +2 -2
- package/frontend/dist/assets/{Dashboard-YMaaIlIZ.js.map → Dashboard-Iq4lJkw-.js.map} +1 -1
- package/frontend/dist/assets/{EndpointCopy-Cpc2WqQz.js → EndpointCopy-BSQygOcu.js} +2 -2
- package/frontend/dist/assets/{EndpointCopy-Cpc2WqQz.js.map → EndpointCopy-BSQygOcu.js.map} +1 -1
- package/frontend/dist/assets/{GroupsPage-DqK5U5rY.js → GroupsPage-B_nc_tkn.js} +2 -2
- package/frontend/dist/assets/{GroupsPage-DqK5U5rY.js.map → GroupsPage-B_nc_tkn.js.map} +1 -1
- package/frontend/dist/assets/{LoginPage-ByV35kun.js → LoginPage-DdxxHO3c.js} +2 -2
- package/frontend/dist/assets/{LoginPage-ByV35kun.js.map → LoginPage-DdxxHO3c.js.map} +1 -1
- package/frontend/dist/assets/{LogsPage-BygFVcaV.js → LogsPage-CgwQu0Zh.js} +2 -2
- package/frontend/dist/assets/{LogsPage-BygFVcaV.js.map → LogsPage-CgwQu0Zh.js.map} +1 -1
- package/frontend/dist/assets/{MarketPage-Cc7QeqSw.js → MarketPage-BX7TZNUO.js} +2 -2
- package/frontend/dist/assets/{MarketPage-Cc7QeqSw.js.map → MarketPage-BX7TZNUO.js.map} +1 -1
- package/frontend/dist/assets/{PromptsPage-DvLm5TW4.js → PromptsPage-Ban8sC8u.js} +2 -2
- package/frontend/dist/assets/{PromptsPage-DvLm5TW4.js.map → PromptsPage-Ban8sC8u.js.map} +1 -1
- package/frontend/dist/assets/{ResourcesPage-Dnn1u2fr.js → ResourcesPage-RBUlnQMZ.js} +2 -2
- package/frontend/dist/assets/{ResourcesPage-Dnn1u2fr.js.map → ResourcesPage-RBUlnQMZ.js.map} +1 -1
- package/frontend/dist/assets/ServersPage-GbDb7jmE.js +37 -0
- package/frontend/dist/assets/ServersPage-GbDb7jmE.js.map +1 -0
- package/frontend/dist/assets/SettingsPage-eqjVV7ZP.js +12 -0
- package/frontend/dist/assets/SettingsPage-eqjVV7ZP.js.map +1 -0
- package/frontend/dist/assets/StatusDot-CnuxXTsd.js +2 -0
- package/frontend/dist/assets/StatusDot-CnuxXTsd.js.map +1 -0
- package/frontend/dist/assets/{ToggleGroup-vRqJ9DIB.js → ToggleGroup-9TIHUIkU.js} +2 -2
- package/frontend/dist/assets/{ToggleGroup-vRqJ9DIB.js.map → ToggleGroup-9TIHUIkU.js.map} +1 -1
- package/frontend/dist/assets/UsersPage-B8zPaI6u.js +2 -0
- package/frontend/dist/assets/UsersPage-B8zPaI6u.js.map +1 -0
- package/frontend/dist/assets/index-9Vmc8uCD.js +3 -0
- package/frontend/dist/assets/index-9Vmc8uCD.js.map +1 -0
- package/frontend/dist/assets/index-KKZS7IbJ.css +1 -0
- package/frontend/dist/assets/{resourceService-BgjWoJFK.js → resourceService-C_tjomqu.js} +2 -2
- package/frontend/dist/assets/{resourceService-BgjWoJFK.js.map → resourceService-C_tjomqu.js.map} +1 -1
- package/frontend/dist/assets/{useServerData-CEGj5OU1.js → useServerData-BX2-qyGg.js} +2 -2
- package/frontend/dist/assets/{useServerData-CEGj5OU1.js.map → useServerData-BX2-qyGg.js.map} +1 -1
- package/frontend/dist/assets/useSettingsData-CN236NdG.js +2 -0
- package/frontend/dist/assets/{useSettingsData-C2-q9Okf.js.map → useSettingsData-CN236NdG.js.map} +1 -1
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/ServersPage-RQCARDfE.js +0 -37
- package/frontend/dist/assets/ServersPage-RQCARDfE.js.map +0 -1
- package/frontend/dist/assets/SettingsPage-CF40R-50.js +0 -12
- package/frontend/dist/assets/SettingsPage-CF40R-50.js.map +0 -1
- package/frontend/dist/assets/StatusDot-CkF9WMhU.js +0 -2
- package/frontend/dist/assets/StatusDot-CkF9WMhU.js.map +0 -1
- package/frontend/dist/assets/UsersPage-DBN5qCeO.js +0 -2
- package/frontend/dist/assets/UsersPage-DBN5qCeO.js.map +0 -1
- package/frontend/dist/assets/index-C7dQApMG.js +0 -3
- package/frontend/dist/assets/index-C7dQApMG.js.map +0 -1
- package/frontend/dist/assets/index-D0OIBhmN.css +0 -1
- package/frontend/dist/assets/useSettingsData-C2-q9Okf.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActivityPage-CLXufyzU.js","sources":["../../src/pages/ActivityPage.tsx"],"sourcesContent":["import React, { useState, useEffect, useCallback } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport {\n Activity,\n ActivityStats,\n ActivityFilter,\n ActivityFilterOptions,\n ActivityStatus,\n} from '@/types';\nimport {\n getActivities,\n getActivityById,\n getActivityStats,\n getActivityFilterOptions,\n deleteOldActivities,\n} from '@/services/activityService';\nimport Pagination from '@/components/ui/Pagination';\n\n// Pagination info type\ninterface PaginationInfo {\n page: number;\n limit: number;\n total: number;\n totalPages: number;\n hasNextPage: boolean;\n hasPrevPage: boolean;\n}\n\nconst STATUS_OPTIONS: ActivityStatus[] = ['success', 'error'];\n\nconst isValidStatus = (value: string): value is ActivityStatus =>\n STATUS_OPTIONS.includes(value as ActivityStatus);\n\nconst ActivityPage: React.FC = () => {\n const { t } = useTranslation();\n\n // State\n const [activities, setActivities] = useState<Activity[]>([]);\n const [stats, setStats] = useState<ActivityStats | null>(null);\n const [filterOptions, setFilterOptions] = useState<ActivityFilterOptions | null>(null);\n const [pagination, setPagination] = useState<PaginationInfo | null>(null);\n const [currentPage, setCurrentPage] = useState(1);\n const [itemsPerPage, setItemsPerPage] = useState(10);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [selectedActivity, setSelectedActivity] = useState<Activity | null>(null);\n const [showDetailModal, setShowDetailModal] = useState(false);\n\n // Filter state\n const [appliedFilters, setAppliedFilters] = useState<ActivityFilter>({});\n const [searchServer, setSearchServer] = useState('');\n const [searchTool, setSearchTool] = useState('');\n const [searchStatus, setSearchStatus] = useState<string>('');\n const [searchGroup, setSearchGroup] = useState('');\n const [searchKeyName, setSearchKeyName] = useState('');\n\n // Fetch data\n const fetchData = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n\n try {\n // Use appliedFilters directly for fetching\n const currentFilter = { ...appliedFilters };\n\n // Fetch activities, stats, and filter options in parallel\n const [activitiesRes, statsRes, optionsRes] = await Promise.all([\n getActivities(currentPage, itemsPerPage, currentFilter),\n getActivityStats(currentFilter),\n getActivityFilterOptions(),\n ]);\n\n if (activitiesRes?.success && Array.isArray(activitiesRes.data)) {\n setActivities(activitiesRes.data);\n if (activitiesRes.pagination) {\n setPagination(activitiesRes.pagination);\n }\n }\n\n if (statsRes?.success && statsRes.data) {\n setStats(statsRes.data);\n }\n\n if (optionsRes?.success && optionsRes.data) {\n setFilterOptions(optionsRes.data);\n }\n } catch (err) {\n console.error('Error fetching activity data:', err);\n setError(t('activity.fetchError'));\n } finally {\n setIsLoading(false);\n }\n }, [currentPage, itemsPerPage, appliedFilters, t]);\n\n useEffect(() => {\n fetchData();\n }, [fetchData]);\n\n useEffect(() => {\n if (!pagination) {\n return;\n }\n\n const totalPages = Math.max(1, pagination.totalPages || 1);\n if (currentPage > totalPages) {\n setCurrentPage(totalPages);\n }\n }, [pagination, currentPage]);\n\n // Handle view activity details\n const handleViewDetails = async (activity: Activity) => {\n try {\n const response = await getActivityById(activity.id);\n if (response?.success && response.data) {\n setSelectedActivity(response.data);\n setShowDetailModal(true);\n }\n } catch (err) {\n console.error('Error fetching activity details:', err);\n }\n };\n\n // Handle cleanup old activities\n const handleCleanup = async () => {\n if (!window.confirm(t('activity.confirmCleanup'))) {\n return;\n }\n\n try {\n const response = await deleteOldActivities(30);\n if (response?.success) {\n alert(t('activity.cleanupSuccess', { count: response.data?.deletedCount || 0 }));\n fetchData();\n }\n } catch (err) {\n console.error('Error cleaning up activities:', err);\n alert(t('activity.cleanupError'));\n }\n };\n\n // Handle search\n const handleSearch = () => {\n const filters: ActivityFilter = {};\n if (searchServer) filters.server = searchServer;\n if (searchTool) filters.tool = searchTool;\n if (searchStatus && isValidStatus(searchStatus)) {\n filters.status = searchStatus;\n }\n if (searchGroup) filters.group = searchGroup;\n if (searchKeyName) filters.keyName = searchKeyName;\n\n setAppliedFilters(filters);\n setCurrentPage(1);\n };\n\n // Handle clear filters\n const handleClearFilters = () => {\n setSearchServer('');\n setSearchTool('');\n setSearchStatus('');\n setSearchGroup('');\n setSearchKeyName('');\n setAppliedFilters({});\n setCurrentPage(1);\n };\n\n // Format duration\n const formatDuration = (ms: number): string => {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`;\n return `${(ms / 60000).toFixed(2)}m`;\n };\n\n // Format timestamp\n const formatTimestamp = (timestamp: string): string => {\n return new Date(timestamp).toLocaleString();\n };\n\n // Parse JSON safely\n const safeParseJSON = (str: string | undefined): any => {\n if (!str) return null;\n try {\n return JSON.parse(str);\n } catch {\n return str;\n }\n };\n\n // Render stats cards\n const renderStats = () => {\n if (!stats) return null;\n\n return (\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-3 mb-4\">\n {[\n { label: t('activity.totalCalls'), value: stats.totalCalls, tone: 'default' as const },\n { label: t('activity.successCount'), value: stats.successCount, tone: 'ok' as const },\n { label: t('activity.errorCount'), value: stats.errorCount, tone: 'err' as const },\n {\n label: t('activity.avgDuration'),\n value: formatDuration(stats.avgDuration),\n tone: 'default' as const,\n },\n ].map((s) => (\n <div key={s.label} className=\"hub-card\" style={{ padding: '12px 14px' }}>\n <div className=\"text-[12px]\" style={{ color: 'var(--hub-ink-3)' }}>\n {s.label}\n </div>\n <div\n className=\"hub-num\"\n style={{\n fontSize: 22,\n fontWeight: 500,\n lineHeight: 1.1,\n marginTop: 6,\n letterSpacing: '-0.02em',\n color:\n s.tone === 'ok'\n ? 'oklch(0.4 0.13 145)'\n : s.tone === 'err'\n ? 'oklch(0.45 0.18 25)'\n : 'var(--hub-ink)',\n }}\n >\n {s.value}\n </div>\n </div>\n ))}\n </div>\n );\n };\n\n // Render filters\n const renderFilters = () => {\n return (\n <div className=\"hub-card px-4 py-3 mb-4\">\n <div className=\"flex flex-wrap gap-3 items-end\">\n <div className=\"flex-1 min-w-[140px]\">\n <label className=\"sr-only\" htmlFor=\"activity-server\">\n {t('activity.server')}\n </label>\n <div className=\"relative\">\n <input\n id=\"activity-server\"\n type=\"text\"\n value={searchServer}\n onChange={(e) => setSearchServer(e.target.value)}\n placeholder={t('activity.searchServer')}\n className=\"hub-input pr-9\"\n list=\"server-options\"\n />\n {searchServer && (\n <button\n onClick={() => setSearchServer('')}\n className=\"absolute inset-y-0 right-2 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300\"\n aria-label={t('common.clear')}\n type=\"button\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n )}\n </div>\n {filterOptions?.servers && (\n <datalist id=\"server-options\">\n {filterOptions.servers.map((s) => (\n <option key={s} value={s} />\n ))}\n </datalist>\n )}\n </div>\n <div className=\"flex-1 min-w-[140px]\">\n <label className=\"sr-only\" htmlFor=\"activity-tool\">\n {t('activity.tool')}\n </label>\n <div className=\"relative\">\n <input\n id=\"activity-tool\"\n type=\"text\"\n value={searchTool}\n onChange={(e) => setSearchTool(e.target.value)}\n placeholder={t('activity.searchTool')}\n className=\"hub-input pr-9\"\n list=\"tool-options\"\n />\n {searchTool && (\n <button\n onClick={() => setSearchTool('')}\n className=\"absolute inset-y-0 right-2 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300\"\n aria-label={t('common.clear')}\n type=\"button\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n )}\n </div>\n {filterOptions?.tools && (\n <datalist id=\"tool-options\">\n {filterOptions.tools.map((t) => (\n <option key={t} value={t} />\n ))}\n </datalist>\n )}\n </div>\n <div className=\"flex-1 min-w-[140px]\">\n <label className=\"sr-only\" htmlFor=\"activity-status\">\n {t('activity.status')}\n </label>\n <div className=\"relative\">\n <input\n id=\"activity-status\"\n type=\"text\"\n value={searchStatus}\n onChange={(e) => setSearchStatus(e.target.value.toLowerCase())}\n placeholder={t('activity.searchStatus')}\n className=\"hub-input pr-9\"\n list=\"activity-status-options\"\n />\n {searchStatus && (\n <button\n onClick={() => setSearchStatus('')}\n className=\"absolute inset-y-0 right-2 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300\"\n aria-label={t('common.clear')}\n type=\"button\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n )}\n </div>\n <datalist id=\"activity-status-options\">\n {STATUS_OPTIONS.map((status) => (\n <option key={status} value={status} />\n ))}\n </datalist>\n </div>\n <div className=\"flex-1 min-w-[140px]\">\n <label className=\"sr-only\" htmlFor=\"activity-group\">\n {t('activity.group')}\n </label>\n <div className=\"relative\">\n <input\n id=\"activity-group\"\n type=\"text\"\n value={searchGroup}\n onChange={(e) => setSearchGroup(e.target.value)}\n placeholder={t('activity.searchGroup')}\n className=\"hub-input pr-9\"\n list=\"group-options\"\n />\n {searchGroup && (\n <button\n onClick={() => setSearchGroup('')}\n className=\"absolute inset-y-0 right-2 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300\"\n aria-label={t('common.clear')}\n type=\"button\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n )}\n </div>\n {filterOptions?.groups && (\n <datalist id=\"group-options\">\n {filterOptions.groups.map((g) => (\n <option key={g} value={g} />\n ))}\n </datalist>\n )}\n </div>\n <div className=\"flex-1 min-w-[140px]\">\n <label className=\"sr-only\" htmlFor=\"activity-keyname\">\n {t('activity.keyName')}\n </label>\n <div className=\"relative\">\n <input\n id=\"activity-keyname\"\n type=\"text\"\n value={searchKeyName}\n onChange={(e) => setSearchKeyName(e.target.value)}\n placeholder={t('activity.searchKeyName')}\n className=\"hub-input pr-9\"\n list=\"keyname-options\"\n />\n {searchKeyName && (\n <button\n onClick={() => setSearchKeyName('')}\n className=\"absolute inset-y-0 right-2 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300\"\n aria-label={t('common.clear')}\n type=\"button\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n )}\n </div>\n {filterOptions?.keyNames && (\n <datalist id=\"keyname-options\">\n {filterOptions.keyNames.map((k) => (\n <option key={k} value={k} />\n ))}\n </datalist>\n )}\n </div>\n <div className=\"flex-shrink-0 flex items-center gap-2\">\n <button onClick={handleSearch} className=\"hub-btn primary whitespace-nowrap\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-3.5 w-3.5\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z\"\n clipRule=\"evenodd\"\n />\n </svg>\n {t('common.search')}\n </button>\n <button onClick={handleClearFilters} className=\"hub-btn whitespace-nowrap\">\n {t('common.clear')}\n </button>\n </div>\n </div>\n </div>\n );\n };\n\n // Render activity table\n const renderActivityTable = () => {\n if (activities.length === 0) {\n return (\n <div className=\"hub-card p-10 text-center\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('activity.noData')}\n </div>\n );\n }\n\n return (\n <div className=\"hub-card overflow-hidden\">\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead style={{ background: 'var(--hub-bg-2)' }}>\n <tr>\n {[\n t('activity.timestamp'),\n t('activity.server'),\n t('activity.tool'),\n t('activity.duration'),\n t('activity.status'),\n t('activity.group'),\n t('activity.key'),\n t('activity.sourceIp'),\n t('common.actions'),\n ].map((label) => (\n <th\n key={label}\n className=\"hub-mono\"\n style={{\n padding: '9px 14px',\n textAlign: 'left',\n fontSize: 11,\n color: 'var(--hub-ink-3)',\n textTransform: 'uppercase',\n letterSpacing: '0.08em',\n fontWeight: 500,\n }}\n >\n {label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {activities.map((activity) => (\n <tr\n key={activity.id}\n className=\"transition-colors hover:bg-[var(--hub-surface-hover)]\"\n style={{ borderTop: '1px solid var(--hub-line-2)' }}\n >\n <td\n className=\"hub-mono whitespace-nowrap\"\n style={{ padding: '10px 14px', fontSize: 12, color: 'var(--hub-ink-2)' }}\n >\n {formatTimestamp(activity.timestamp)}\n </td>\n <td style={{ padding: '10px 14px' }}>\n <span className=\"hub-tag\">{activity.server}</span>\n </td>\n <td style={{ padding: '10px 14px' }}>\n <span className=\"hub-tag accent\">{activity.tool}</span>\n </td>\n <td\n className=\"hub-mono hub-num whitespace-nowrap\"\n style={{ padding: '10px 14px', fontSize: 12, color: 'var(--hub-ink-2)' }}\n >\n {formatDuration(activity.duration)}\n </td>\n <td style={{ padding: '10px 14px' }} className=\"whitespace-nowrap\">\n <span\n className={`hub-status ${activity.status === 'success' ? 'ok' : 'err'}`}\n >\n <span className=\"hub-dot\" />\n {activity.status === 'success'\n ? t('activity.statusSuccess')\n : t('activity.statusError')}\n </span>\n </td>\n <td\n style={{ padding: '10px 14px', fontSize: 12, color: 'var(--hub-ink-3)' }}\n >\n {activity.group || '—'}\n </td>\n <td\n style={{ padding: '10px 14px', fontSize: 12, color: 'var(--hub-ink-3)' }}\n >\n {activity.keyName || '—'}\n </td>\n <td\n className=\"hub-mono whitespace-nowrap\"\n style={{ padding: '10px 14px', fontSize: 12, color: 'var(--hub-ink-3)' }}\n >\n {activity.sourceIp || '—'}\n </td>\n <td style={{ padding: '10px 14px' }}>\n <button\n onClick={() => handleViewDetails(activity)}\n className=\"hub-btn ghost sm\"\n style={{ color: 'var(--hub-accent)' }}\n >\n {t('common.view')}\n </button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n );\n };\n\n // Render detail modal\n const renderDetailModal = () => {\n if (!showDetailModal || !selectedActivity) return null;\n\n const inputData = safeParseJSON(selectedActivity.input);\n const outputData = safeParseJSON(selectedActivity.output);\n\n return (\n <div className=\"fixed inset-0 bg-black/50 flex items-center justify-center z-50\">\n <div\n className=\"hub-card max-w-4xl w-full mx-4 max-h-[90vh] overflow-hidden\"\n style={{ boxShadow: '0 8px 24px rgba(0,0,0,0.12)' }}\n >\n <div\n className=\"flex items-center justify-between px-5 py-3\"\n style={{ borderBottom: '1px solid var(--hub-line-2)' }}\n >\n <h3 className=\"hub-card-title\">{t('activity.details')}</h3>\n <button\n onClick={() => setShowDetailModal(false)}\n className=\"hub-icon-btn sm\"\n aria-label=\"close\"\n >\n <svg className=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n <div className=\"px-6 py-4 overflow-y-auto max-h-[calc(90vh-120px)]\">\n <div className=\"grid grid-cols-2 gap-4 mb-4\">\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.timestamp')}\n </label>\n <p className=\"text-gray-900 dark:text-white\">\n {formatTimestamp(selectedActivity.timestamp)}\n </p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.duration')}\n </label>\n <p className=\"text-gray-900 dark:text-white\">\n {formatDuration(selectedActivity.duration)}\n </p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.server')}\n </label>\n <p className=\"text-gray-900 dark:text-white font-mono\">{selectedActivity.server}</p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.tool')}\n </label>\n <p className=\"text-gray-900 dark:text-white font-mono\">{selectedActivity.tool}</p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.status')}\n </label>\n <span\n className={`px-2 py-1 rounded text-xs font-medium ${\n selectedActivity.status === 'success'\n ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'\n : 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'\n }`}\n >\n {selectedActivity.status === 'success'\n ? t('activity.statusSuccess')\n : t('activity.statusError')}\n </span>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.group')}\n </label>\n <p className=\"text-gray-900 dark:text-white\">{selectedActivity.group || '-'}</p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.sourceIp')}\n </label>\n <p className=\"text-gray-900 dark:text-white font-mono\">\n {selectedActivity.sourceIp || '-'}\n </p>\n </div>\n {selectedActivity.keyName && (\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.key')}\n </label>\n <p className=\"text-gray-900 dark:text-white\">{selectedActivity.keyName}</p>\n </div>\n )}\n </div>\n\n {selectedActivity.errorMessage && (\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-red-500 mb-1\">\n {t('activity.errorMessage')}\n </label>\n <div className=\"bg-red-50 dark:bg-red-900/20 rounded p-3 text-sm text-red-800 dark:text-red-200\">\n {selectedActivity.errorMessage}\n </div>\n </div>\n )}\n\n {inputData && (\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400 mb-1\">\n {t('activity.input')}\n </label>\n <pre className=\"bg-gray-100 dark:bg-gray-700 rounded p-3 text-sm overflow-x-auto max-h-64\">\n {typeof inputData === 'string' ? inputData : JSON.stringify(inputData, null, 2)}\n </pre>\n </div>\n )}\n\n {outputData && (\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400 mb-1\">\n {t('activity.output')}\n </label>\n <pre className=\"bg-gray-100 dark:bg-gray-700 rounded p-3 text-sm overflow-x-auto max-h-64\">\n {typeof outputData === 'string'\n ? outputData\n : JSON.stringify(outputData, null, 2)}\n </pre>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n };\n\n return (\n <div>\n <div className=\"flex items-end justify-between gap-4 mb-6\">\n <div>\n <h1 className=\"hub-h1\">{t('activity.title')}</h1>\n <p className=\"hub-sub\">\n <span className=\"hub-num\">{pagination?.total ?? activities.length}</span> entries\n </p>\n </div>\n <button onClick={handleCleanup} className=\"hub-btn danger\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-3.5 w-3.5\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z\"\n clipRule=\"evenodd\"\n />\n </svg>\n {t('activity.cleanup')}\n </button>\n </div>\n\n {error && (\n <div\n className=\"hub-card flex items-center justify-between gap-3 mb-4\"\n style={{\n padding: '10px 14px',\n borderColor: 'oklch(0.85 0.1 25)',\n background: 'oklch(0.97 0.03 25)',\n color: 'oklch(0.4 0.18 25)',\n }}\n >\n <span className=\"truncate text-[13px]\">{error}</span>\n <button className=\"hub-icon-btn sm\" onClick={() => setError(null)} aria-label=\"close\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-3 w-3\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n </div>\n )}\n\n {isLoading && activities.length === 0 ? (\n <div className=\"hub-card p-10 text-center\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('app.loading')}\n </div>\n ) : (\n <>\n {renderStats()}\n {renderFilters()}\n {renderActivityTable()}\n\n {/* Pagination */}\n <div className=\"flex items-center mt-6\">\n <div className=\"flex-[2] text-sm text-gray-500 dark:text-gray-400\">\n {pagination &&\n t('common.showing', {\n start: (pagination.page - 1) * pagination.limit + 1,\n end: Math.min(pagination.page * pagination.limit, pagination.total),\n total: pagination.total,\n })}\n </div>\n <div className=\"flex-[4] flex justify-center\">\n {pagination && pagination.totalPages > 1 && (\n <Pagination\n currentPage={currentPage}\n totalPages={pagination.totalPages}\n onPageChange={setCurrentPage}\n disabled={isLoading}\n />\n )}\n </div>\n <div className=\"flex-[2] flex items-center justify-end space-x-2\">\n <label htmlFor=\"perPage\" className=\"text-sm text-gray-500 dark:text-gray-400\">\n {t('common.itemsPerPage')}:\n </label>\n <select\n id=\"perPage\"\n value={itemsPerPage}\n onChange={(e) => {\n setItemsPerPage(Number(e.target.value));\n setCurrentPage(1);\n }}\n disabled={isLoading}\n className=\"border border-gray-300 dark:border-gray-600 rounded p-1 text-sm dark:bg-gray-700 dark:text-white outline-none disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <option value={10}>10</option>\n <option value={20}>20</option>\n <option value={50}>50</option>\n <option value={100}>100</option>\n </select>\n </div>\n </div>\n </>\n )}\n\n {renderDetailModal()}\n </div>\n );\n};\n\nexport default ActivityPage;\n"],"names":["STATUS_OPTIONS","isValidStatus","value","ActivityPage","t","useTranslation","activities","setActivities","useState","stats","setStats","filterOptions","setFilterOptions","pagination","setPagination","currentPage","setCurrentPage","itemsPerPage","setItemsPerPage","isLoading","setIsLoading","error","setError","selectedActivity","setSelectedActivity","showDetailModal","setShowDetailModal","appliedFilters","setAppliedFilters","searchServer","setSearchServer","searchTool","setSearchTool","searchStatus","setSearchStatus","searchGroup","setSearchGroup","searchKeyName","setSearchKeyName","fetchData","useCallback","currentFilter","activitiesRes","statsRes","optionsRes","getActivities","getActivityStats","getActivityFilterOptions","err","useEffect","totalPages","handleViewDetails","activity","response","getActivityById","handleCleanup","deleteOldActivities","_a","handleSearch","filters","handleClearFilters","formatDuration","ms","formatTimestamp","timestamp","safeParseJSON","str","renderStats","jsx","s","jsxs","renderFilters","e","status","g","k","renderActivityTable","label","renderDetailModal","inputData","outputData","Fragment","Pagination"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ActivityPage-CjUx2EbK.js","sources":["../../src/pages/ActivityPage.tsx"],"sourcesContent":["import React, { useState, useEffect, useCallback } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport {\n Activity,\n ActivityStats,\n ActivityFilter,\n ActivityFilterOptions,\n ActivityStatus,\n} from '@/types';\nimport {\n getActivities,\n getActivityById,\n getActivityStats,\n getActivityFilterOptions,\n deleteOldActivities,\n} from '@/services/activityService';\nimport Pagination from '@/components/ui/Pagination';\n\n// Pagination info type\ninterface PaginationInfo {\n page: number;\n limit: number;\n total: number;\n totalPages: number;\n hasNextPage: boolean;\n hasPrevPage: boolean;\n}\n\nconst STATUS_OPTIONS: ActivityStatus[] = ['success', 'error'];\n\nconst isValidStatus = (value: string): value is ActivityStatus =>\n STATUS_OPTIONS.includes(value as ActivityStatus);\n\nconst ActivityPage: React.FC = () => {\n const { t } = useTranslation();\n\n // State\n const [activities, setActivities] = useState<Activity[]>([]);\n const [stats, setStats] = useState<ActivityStats | null>(null);\n const [filterOptions, setFilterOptions] = useState<ActivityFilterOptions | null>(null);\n const [pagination, setPagination] = useState<PaginationInfo | null>(null);\n const [currentPage, setCurrentPage] = useState(1);\n const [itemsPerPage, setItemsPerPage] = useState(10);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [selectedActivity, setSelectedActivity] = useState<Activity | null>(null);\n const [showDetailModal, setShowDetailModal] = useState(false);\n\n // Filter state\n const [appliedFilters, setAppliedFilters] = useState<ActivityFilter>({});\n const [searchServer, setSearchServer] = useState('');\n const [searchTool, setSearchTool] = useState('');\n const [searchStatus, setSearchStatus] = useState<string>('');\n const [searchGroup, setSearchGroup] = useState('');\n const [searchKeyName, setSearchKeyName] = useState('');\n\n // Fetch data\n const fetchData = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n\n try {\n // Use appliedFilters directly for fetching\n const currentFilter = { ...appliedFilters };\n\n // Fetch activities, stats, and filter options in parallel\n const [activitiesRes, statsRes, optionsRes] = await Promise.all([\n getActivities(currentPage, itemsPerPage, currentFilter),\n getActivityStats(currentFilter),\n getActivityFilterOptions(),\n ]);\n\n if (activitiesRes?.success && Array.isArray(activitiesRes.data)) {\n setActivities(activitiesRes.data);\n if (activitiesRes.pagination) {\n setPagination(activitiesRes.pagination);\n }\n }\n\n if (statsRes?.success && statsRes.data) {\n setStats(statsRes.data);\n }\n\n if (optionsRes?.success && optionsRes.data) {\n setFilterOptions(optionsRes.data);\n }\n } catch (err) {\n console.error('Error fetching activity data:', err);\n setError(t('activity.fetchError'));\n } finally {\n setIsLoading(false);\n }\n }, [currentPage, itemsPerPage, appliedFilters, t]);\n\n useEffect(() => {\n fetchData();\n }, [fetchData]);\n\n useEffect(() => {\n if (!pagination) {\n return;\n }\n\n const totalPages = Math.max(1, pagination.totalPages || 1);\n if (currentPage > totalPages) {\n setCurrentPage(totalPages);\n }\n }, [pagination, currentPage]);\n\n // Handle view activity details\n const handleViewDetails = async (activity: Activity) => {\n try {\n const response = await getActivityById(activity.id);\n if (response?.success && response.data) {\n setSelectedActivity(response.data);\n setShowDetailModal(true);\n }\n } catch (err) {\n console.error('Error fetching activity details:', err);\n }\n };\n\n // Handle cleanup old activities\n const handleCleanup = async () => {\n if (!window.confirm(t('activity.confirmCleanup'))) {\n return;\n }\n\n try {\n const response = await deleteOldActivities(30);\n if (response?.success) {\n alert(t('activity.cleanupSuccess', { count: response.data?.deletedCount || 0 }));\n fetchData();\n }\n } catch (err) {\n console.error('Error cleaning up activities:', err);\n alert(t('activity.cleanupError'));\n }\n };\n\n // Handle search\n const handleSearch = () => {\n const filters: ActivityFilter = {};\n if (searchServer) filters.server = searchServer;\n if (searchTool) filters.tool = searchTool;\n if (searchStatus && isValidStatus(searchStatus)) {\n filters.status = searchStatus;\n }\n if (searchGroup) filters.group = searchGroup;\n if (searchKeyName) filters.keyName = searchKeyName;\n\n setAppliedFilters(filters);\n setCurrentPage(1);\n };\n\n // Handle clear filters\n const handleClearFilters = () => {\n setSearchServer('');\n setSearchTool('');\n setSearchStatus('');\n setSearchGroup('');\n setSearchKeyName('');\n setAppliedFilters({});\n setCurrentPage(1);\n };\n\n // Format duration\n const formatDuration = (ms: number): string => {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`;\n return `${(ms / 60000).toFixed(2)}m`;\n };\n\n // Format timestamp\n const formatTimestamp = (timestamp: string): string => {\n return new Date(timestamp).toLocaleString();\n };\n\n // Parse JSON safely\n const safeParseJSON = (str: string | undefined): any => {\n if (!str) return null;\n try {\n return JSON.parse(str);\n } catch {\n return str;\n }\n };\n\n // Render stats cards\n const renderStats = () => {\n if (!stats) return null;\n\n return (\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-3 mb-4\">\n {[\n { label: t('activity.totalCalls'), value: stats.totalCalls, tone: 'default' as const },\n { label: t('activity.successCount'), value: stats.successCount, tone: 'ok' as const },\n { label: t('activity.errorCount'), value: stats.errorCount, tone: 'err' as const },\n {\n label: t('activity.avgDuration'),\n value: formatDuration(stats.avgDuration),\n tone: 'default' as const,\n },\n ].map((s) => (\n <div key={s.label} className=\"hub-card\" style={{ padding: '12px 14px' }}>\n <div className=\"text-[12px]\" style={{ color: 'var(--hub-ink-3)' }}>\n {s.label}\n </div>\n <div\n className=\"hub-num\"\n style={{\n fontSize: 22,\n fontWeight: 500,\n lineHeight: 1.1,\n marginTop: 6,\n letterSpacing: '-0.02em',\n color:\n s.tone === 'ok'\n ? 'oklch(0.4 0.13 145)'\n : s.tone === 'err'\n ? 'oklch(0.45 0.18 25)'\n : 'var(--hub-ink)',\n }}\n >\n {s.value}\n </div>\n </div>\n ))}\n </div>\n );\n };\n\n // Render filters\n const renderFilters = () => {\n return (\n <div className=\"hub-card px-4 py-3 mb-4\">\n <div className=\"flex flex-wrap gap-3 items-end\">\n <div className=\"flex-1 min-w-[140px]\">\n <label className=\"sr-only\" htmlFor=\"activity-server\">\n {t('activity.server')}\n </label>\n <div className=\"relative\">\n <input\n id=\"activity-server\"\n type=\"text\"\n value={searchServer}\n onChange={(e) => setSearchServer(e.target.value)}\n placeholder={t('activity.searchServer')}\n className=\"hub-input pr-9\"\n list=\"server-options\"\n />\n {searchServer && (\n <button\n onClick={() => setSearchServer('')}\n className=\"absolute inset-y-0 right-2 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300\"\n aria-label={t('common.clear')}\n type=\"button\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n )}\n </div>\n {filterOptions?.servers && (\n <datalist id=\"server-options\">\n {filterOptions.servers.map((s) => (\n <option key={s} value={s} />\n ))}\n </datalist>\n )}\n </div>\n <div className=\"flex-1 min-w-[140px]\">\n <label className=\"sr-only\" htmlFor=\"activity-tool\">\n {t('activity.tool')}\n </label>\n <div className=\"relative\">\n <input\n id=\"activity-tool\"\n type=\"text\"\n value={searchTool}\n onChange={(e) => setSearchTool(e.target.value)}\n placeholder={t('activity.searchTool')}\n className=\"hub-input pr-9\"\n list=\"tool-options\"\n />\n {searchTool && (\n <button\n onClick={() => setSearchTool('')}\n className=\"absolute inset-y-0 right-2 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300\"\n aria-label={t('common.clear')}\n type=\"button\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n )}\n </div>\n {filterOptions?.tools && (\n <datalist id=\"tool-options\">\n {filterOptions.tools.map((t) => (\n <option key={t} value={t} />\n ))}\n </datalist>\n )}\n </div>\n <div className=\"flex-1 min-w-[140px]\">\n <label className=\"sr-only\" htmlFor=\"activity-status\">\n {t('activity.status')}\n </label>\n <div className=\"relative\">\n <input\n id=\"activity-status\"\n type=\"text\"\n value={searchStatus}\n onChange={(e) => setSearchStatus(e.target.value.toLowerCase())}\n placeholder={t('activity.searchStatus')}\n className=\"hub-input pr-9\"\n list=\"activity-status-options\"\n />\n {searchStatus && (\n <button\n onClick={() => setSearchStatus('')}\n className=\"absolute inset-y-0 right-2 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300\"\n aria-label={t('common.clear')}\n type=\"button\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n )}\n </div>\n <datalist id=\"activity-status-options\">\n {STATUS_OPTIONS.map((status) => (\n <option key={status} value={status} />\n ))}\n </datalist>\n </div>\n <div className=\"flex-1 min-w-[140px]\">\n <label className=\"sr-only\" htmlFor=\"activity-group\">\n {t('activity.group')}\n </label>\n <div className=\"relative\">\n <input\n id=\"activity-group\"\n type=\"text\"\n value={searchGroup}\n onChange={(e) => setSearchGroup(e.target.value)}\n placeholder={t('activity.searchGroup')}\n className=\"hub-input pr-9\"\n list=\"group-options\"\n />\n {searchGroup && (\n <button\n onClick={() => setSearchGroup('')}\n className=\"absolute inset-y-0 right-2 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300\"\n aria-label={t('common.clear')}\n type=\"button\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n )}\n </div>\n {filterOptions?.groups && (\n <datalist id=\"group-options\">\n {filterOptions.groups.map((g) => (\n <option key={g} value={g} />\n ))}\n </datalist>\n )}\n </div>\n <div className=\"flex-1 min-w-[140px]\">\n <label className=\"sr-only\" htmlFor=\"activity-keyname\">\n {t('activity.keyName')}\n </label>\n <div className=\"relative\">\n <input\n id=\"activity-keyname\"\n type=\"text\"\n value={searchKeyName}\n onChange={(e) => setSearchKeyName(e.target.value)}\n placeholder={t('activity.searchKeyName')}\n className=\"hub-input pr-9\"\n list=\"keyname-options\"\n />\n {searchKeyName && (\n <button\n onClick={() => setSearchKeyName('')}\n className=\"absolute inset-y-0 right-2 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300\"\n aria-label={t('common.clear')}\n type=\"button\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n )}\n </div>\n {filterOptions?.keyNames && (\n <datalist id=\"keyname-options\">\n {filterOptions.keyNames.map((k) => (\n <option key={k} value={k} />\n ))}\n </datalist>\n )}\n </div>\n <div className=\"flex-shrink-0 flex items-center gap-2\">\n <button onClick={handleSearch} className=\"hub-btn primary whitespace-nowrap\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-3.5 w-3.5\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z\"\n clipRule=\"evenodd\"\n />\n </svg>\n {t('common.search')}\n </button>\n <button onClick={handleClearFilters} className=\"hub-btn whitespace-nowrap\">\n {t('common.clear')}\n </button>\n </div>\n </div>\n </div>\n );\n };\n\n // Render activity table\n const renderActivityTable = () => {\n if (activities.length === 0) {\n return (\n <div className=\"hub-card p-10 text-center\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('activity.noData')}\n </div>\n );\n }\n\n return (\n <div className=\"hub-card overflow-hidden\">\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead style={{ background: 'var(--hub-bg-2)' }}>\n <tr>\n {[\n t('activity.timestamp'),\n t('activity.server'),\n t('activity.tool'),\n t('activity.duration'),\n t('activity.status'),\n t('activity.group'),\n t('activity.key'),\n t('activity.sourceIp'),\n t('common.actions'),\n ].map((label) => (\n <th\n key={label}\n className=\"hub-mono\"\n style={{\n padding: '9px 14px',\n textAlign: 'left',\n fontSize: 11,\n color: 'var(--hub-ink-3)',\n textTransform: 'uppercase',\n letterSpacing: '0.08em',\n fontWeight: 500,\n }}\n >\n {label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {activities.map((activity) => (\n <tr\n key={activity.id}\n className=\"transition-colors hover:bg-[var(--hub-surface-hover)]\"\n style={{ borderTop: '1px solid var(--hub-line-2)' }}\n >\n <td\n className=\"hub-mono whitespace-nowrap\"\n style={{ padding: '10px 14px', fontSize: 12, color: 'var(--hub-ink-2)' }}\n >\n {formatTimestamp(activity.timestamp)}\n </td>\n <td style={{ padding: '10px 14px' }}>\n <span className=\"hub-tag\">{activity.server}</span>\n </td>\n <td style={{ padding: '10px 14px' }}>\n <span className=\"hub-tag accent\">{activity.tool}</span>\n </td>\n <td\n className=\"hub-mono hub-num whitespace-nowrap\"\n style={{ padding: '10px 14px', fontSize: 12, color: 'var(--hub-ink-2)' }}\n >\n {formatDuration(activity.duration)}\n </td>\n <td style={{ padding: '10px 14px' }} className=\"whitespace-nowrap\">\n <span\n className={`hub-status ${activity.status === 'success' ? 'ok' : 'err'}`}\n >\n <span className=\"hub-dot\" />\n {activity.status === 'success'\n ? t('activity.statusSuccess')\n : t('activity.statusError')}\n </span>\n </td>\n <td\n style={{ padding: '10px 14px', fontSize: 12, color: 'var(--hub-ink-3)' }}\n >\n {activity.group || '—'}\n </td>\n <td\n style={{ padding: '10px 14px', fontSize: 12, color: 'var(--hub-ink-3)' }}\n >\n {activity.keyName || '—'}\n </td>\n <td\n className=\"hub-mono whitespace-nowrap\"\n style={{ padding: '10px 14px', fontSize: 12, color: 'var(--hub-ink-3)' }}\n >\n {activity.sourceIp || '—'}\n </td>\n <td style={{ padding: '10px 14px' }}>\n <button\n onClick={() => handleViewDetails(activity)}\n className=\"hub-btn ghost sm\"\n style={{ color: 'var(--hub-accent)' }}\n >\n {t('common.view')}\n </button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n );\n };\n\n // Render detail modal\n const renderDetailModal = () => {\n if (!showDetailModal || !selectedActivity) return null;\n\n const inputData = safeParseJSON(selectedActivity.input);\n const outputData = safeParseJSON(selectedActivity.output);\n\n return (\n <div className=\"fixed inset-0 bg-black/50 flex items-center justify-center z-50\">\n <div\n className=\"hub-card max-w-4xl w-full mx-4 max-h-[90vh] overflow-hidden\"\n style={{ boxShadow: '0 8px 24px rgba(0,0,0,0.12)' }}\n >\n <div\n className=\"flex items-center justify-between px-5 py-3\"\n style={{ borderBottom: '1px solid var(--hub-line-2)' }}\n >\n <h3 className=\"hub-card-title\">{t('activity.details')}</h3>\n <button\n onClick={() => setShowDetailModal(false)}\n className=\"hub-icon-btn sm\"\n aria-label=\"close\"\n >\n <svg className=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n <div className=\"px-6 py-4 overflow-y-auto max-h-[calc(90vh-120px)]\">\n <div className=\"grid grid-cols-2 gap-4 mb-4\">\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.timestamp')}\n </label>\n <p className=\"text-gray-900 dark:text-white\">\n {formatTimestamp(selectedActivity.timestamp)}\n </p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.duration')}\n </label>\n <p className=\"text-gray-900 dark:text-white\">\n {formatDuration(selectedActivity.duration)}\n </p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.server')}\n </label>\n <p className=\"text-gray-900 dark:text-white font-mono\">{selectedActivity.server}</p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.tool')}\n </label>\n <p className=\"text-gray-900 dark:text-white font-mono\">{selectedActivity.tool}</p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.status')}\n </label>\n <span\n className={`px-2 py-1 rounded text-xs font-medium ${\n selectedActivity.status === 'success'\n ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'\n : 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'\n }`}\n >\n {selectedActivity.status === 'success'\n ? t('activity.statusSuccess')\n : t('activity.statusError')}\n </span>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.group')}\n </label>\n <p className=\"text-gray-900 dark:text-white\">{selectedActivity.group || '-'}</p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.sourceIp')}\n </label>\n <p className=\"text-gray-900 dark:text-white font-mono\">\n {selectedActivity.sourceIp || '-'}\n </p>\n </div>\n {selectedActivity.keyName && (\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400\">\n {t('activity.key')}\n </label>\n <p className=\"text-gray-900 dark:text-white\">{selectedActivity.keyName}</p>\n </div>\n )}\n </div>\n\n {selectedActivity.errorMessage && (\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-red-500 mb-1\">\n {t('activity.errorMessage')}\n </label>\n <div className=\"bg-red-50 dark:bg-red-900/20 rounded p-3 text-sm text-red-800 dark:text-red-200\">\n {selectedActivity.errorMessage}\n </div>\n </div>\n )}\n\n {inputData && (\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400 mb-1\">\n {t('activity.input')}\n </label>\n <pre className=\"bg-gray-100 dark:bg-gray-700 rounded p-3 text-sm overflow-x-auto max-h-64\">\n {typeof inputData === 'string' ? inputData : JSON.stringify(inputData, null, 2)}\n </pre>\n </div>\n )}\n\n {outputData && (\n <div>\n <label className=\"block text-sm font-medium text-gray-500 dark:text-gray-400 mb-1\">\n {t('activity.output')}\n </label>\n <pre className=\"bg-gray-100 dark:bg-gray-700 rounded p-3 text-sm overflow-x-auto max-h-64\">\n {typeof outputData === 'string'\n ? outputData\n : JSON.stringify(outputData, null, 2)}\n </pre>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n };\n\n return (\n <div>\n <div className=\"flex items-end justify-between gap-4 mb-6\">\n <div>\n <h1 className=\"hub-h1\">{t('activity.title')}</h1>\n <p className=\"hub-sub\">\n <span className=\"hub-num\">{pagination?.total ?? activities.length}</span> entries\n </p>\n </div>\n <button onClick={handleCleanup} className=\"hub-btn danger\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-3.5 w-3.5\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z\"\n clipRule=\"evenodd\"\n />\n </svg>\n {t('activity.cleanup')}\n </button>\n </div>\n\n {error && (\n <div\n className=\"hub-card flex items-center justify-between gap-3 mb-4\"\n style={{\n padding: '10px 14px',\n borderColor: 'oklch(0.85 0.1 25)',\n background: 'oklch(0.97 0.03 25)',\n color: 'oklch(0.4 0.18 25)',\n }}\n >\n <span className=\"truncate text-[13px]\">{error}</span>\n <button className=\"hub-icon-btn sm\" onClick={() => setError(null)} aria-label=\"close\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-3 w-3\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n </div>\n )}\n\n {isLoading && activities.length === 0 ? (\n <div className=\"hub-card p-10 text-center\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('app.loading')}\n </div>\n ) : (\n <>\n {renderStats()}\n {renderFilters()}\n {renderActivityTable()}\n\n {/* Pagination */}\n <div className=\"flex items-center mt-6\">\n <div className=\"flex-[2] text-sm text-gray-500 dark:text-gray-400\">\n {pagination &&\n t('common.showing', {\n start: (pagination.page - 1) * pagination.limit + 1,\n end: Math.min(pagination.page * pagination.limit, pagination.total),\n total: pagination.total,\n })}\n </div>\n <div className=\"flex-[4] flex justify-center\">\n {pagination && pagination.totalPages > 1 && (\n <Pagination\n currentPage={currentPage}\n totalPages={pagination.totalPages}\n onPageChange={setCurrentPage}\n disabled={isLoading}\n />\n )}\n </div>\n <div className=\"flex-[2] flex items-center justify-end space-x-2\">\n <label htmlFor=\"perPage\" className=\"text-sm text-gray-500 dark:text-gray-400\">\n {t('common.itemsPerPage')}:\n </label>\n <select\n id=\"perPage\"\n value={itemsPerPage}\n onChange={(e) => {\n setItemsPerPage(Number(e.target.value));\n setCurrentPage(1);\n }}\n disabled={isLoading}\n className=\"border border-gray-300 dark:border-gray-600 rounded p-1 text-sm dark:bg-gray-700 dark:text-white outline-none disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <option value={10}>10</option>\n <option value={20}>20</option>\n <option value={50}>50</option>\n <option value={100}>100</option>\n </select>\n </div>\n </div>\n </>\n )}\n\n {renderDetailModal()}\n </div>\n );\n};\n\nexport default ActivityPage;\n"],"names":["STATUS_OPTIONS","isValidStatus","value","ActivityPage","t","useTranslation","activities","setActivities","useState","stats","setStats","filterOptions","setFilterOptions","pagination","setPagination","currentPage","setCurrentPage","itemsPerPage","setItemsPerPage","isLoading","setIsLoading","error","setError","selectedActivity","setSelectedActivity","showDetailModal","setShowDetailModal","appliedFilters","setAppliedFilters","searchServer","setSearchServer","searchTool","setSearchTool","searchStatus","setSearchStatus","searchGroup","setSearchGroup","searchKeyName","setSearchKeyName","fetchData","useCallback","currentFilter","activitiesRes","statsRes","optionsRes","getActivities","getActivityStats","getActivityFilterOptions","err","useEffect","totalPages","handleViewDetails","activity","response","getActivityById","handleCleanup","deleteOldActivities","_a","handleSearch","filters","handleClearFilters","formatDuration","ms","formatTimestamp","timestamp","safeParseJSON","str","renderStats","jsx","s","jsxs","renderFilters","e","status","g","k","renderActivityTable","label","renderDetailModal","inputData","outputData","Fragment","Pagination"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{b as $,R as u,r as S,j as e}from"./framework-vendor-BUhDPOUZ.js";import{u as D}from"./useServerData-
|
|
2
|
-
//# sourceMappingURL=Dashboard-
|
|
1
|
+
import{b as $,R as u,r as S,j as e}from"./framework-vendor-BUhDPOUZ.js";import{u as D}from"./useServerData-BX2-qyGg.js";import{d as L}from"./index-9Vmc8uCD.js";import{u as T}from"./useSettingsData-CN236NdG.js";import{E as h}from"./EndpointCopy-BSQygOcu.js";import{S as M}from"./StatusDot-CnuxXTsd.js";import{u as P}from"./i18n-vendor-Kbr87Ofu.js";import{R as A,P as U,j as y,k as H}from"./icons-vendor-CKgJB3SC.js";const c=({label:s,value:l,tone:a="default"})=>{const t=a==="ok"?"oklch(0.4 0.13 145)":a==="warn"?"oklch(0.45 0.13 80)":a==="err"?"oklch(0.45 0.18 25)":a==="muted"?"var(--hub-ink-3)":"var(--hub-ink)";return e.jsxs("div",{className:"hub-card",style:{padding:"14px 16px"},children:[e.jsx("div",{className:"text-[12px]",style:{color:"var(--hub-ink-3)"},children:s}),e.jsx("div",{className:"hub-num",style:{fontSize:26,fontWeight:500,letterSpacing:"-0.02em",lineHeight:1.1,marginTop:8,color:t},children:l})]})},w=(s,l)=>l?l==="stdio"?s("server.typeStdio")||"stdio":l==="sse"?s("server.typeSse")||"sse":l==="streamable-http"?s("server.typeStreamableHttp")||"http":l==="openapi"?s("server.typeOpenapi")||"openapi":l:null,I=()=>{var j;const{t:s}=P(),l=$(),{allServers:a,error:t,setError:C,isLoading:p,triggerRefresh:R}=D({refreshOnMount:!0}),{groups:b}=L(),{installConfig:m}=T(),[z,x]=u.useState(!1),v=u.useRef(!1);u.useEffect(()=>{if(p){v.current=!0;return}if(v.current){x(!0);return}(a.length>0||t)&&x(!0)},[p,a.length,t]);const n=S.useMemo(()=>({total:a.length,online:a.filter(r=>r.status==="connected").length,disabled:a.filter(r=>r.enabled===!1).length,offline:a.filter(r=>r.status==="disconnected"&&r.enabled!==!1).length,connecting:a.filter(r=>(r.status==="connecting"||r.status==="oauth_required")&&r.enabled!==!1).length,tools:a.reduce((r,i)=>{var o;return r+(((o=i.tools)==null?void 0:o.length)||0)},0)}),[a]),E=S.useMemo(()=>a.slice(0,6),[a]),d=((j=m==null?void 0:m.baseUrl)==null?void 0:j.replace(/\/+$/,""))||"",g="minmax(220px,1.9fr) minmax(110px,0.95fr) minmax(120px,0.95fr) 80px 80px 90px 72px",f=!z;return e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-end justify-between gap-4 mb-6",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"hub-h1",children:s("pages.dashboard.title")}),e.jsxs("p",{className:"hub-sub",children:[s("pages.dashboard.totalServers")," · ",e.jsx("span",{className:"hub-num",children:n.total})," · ",s("pages.dashboard.onlineServers")," · ",e.jsx("span",{className:"hub-num",children:n.online})]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{className:"hub-btn",onClick:()=>R(),children:[e.jsx(A,{size:13})," ",s("common.refresh")]}),e.jsxs("button",{className:"hub-btn primary",onClick:()=>l("/servers"),children:[e.jsx(U,{size:13})," ",s("server.add")]})]})]}),t&&e.jsxs("div",{className:"hub-card flex items-center justify-between gap-3 mb-5",style:{padding:"10px 14px",borderColor:"oklch(0.85 0.1 25)",background:"oklch(0.97 0.03 25)",color:"oklch(0.4 0.18 25)"},children:[e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[e.jsx(y,{size:14,className:"flex-shrink-0"}),e.jsx("span",{className:"truncate text-[13px]",children:t})]}),e.jsx("button",{className:"hub-icon-btn sm",onClick:()=>C(null),"aria-label":s("app.closeButton"),children:"✕"})]}),f?e.jsx("div",{className:"grid grid-cols-2 md:grid-cols-5 gap-3 mb-6",children:Array.from({length:5}).map((r,i)=>e.jsx("div",{className:"hub-card animate-pulse",style:{padding:"14px 16px",height:78}},i))}):e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-5 gap-3 mb-6",children:[e.jsx(c,{label:s("pages.dashboard.totalServers"),value:n.total}),e.jsx(c,{label:s("pages.dashboard.onlineServers"),value:n.online,tone:"ok"}),e.jsx(c,{label:s("pages.dashboard.connectingServers"),value:n.connecting,tone:"warn"}),e.jsx(c,{label:s("pages.dashboard.offlineServers"),value:n.offline,tone:"err"}),e.jsx(c,{label:s("pages.dashboard.disabledServers"),value:n.disabled,tone:"muted"})]}),a.length>0&&!f&&e.jsxs("div",{className:"hub-card overflow-hidden mb-6",children:[e.jsxs("div",{className:"flex items-center justify-between px-4 py-3",style:{borderBottom:"1px solid var(--hub-line-2)"},children:[e.jsx("h3",{className:"hub-card-title",children:s("pages.dashboard.recentServers")}),e.jsxs("button",{className:"hub-btn ghost sm",style:{color:"var(--hub-ink-3)"},onClick:()=>l("/servers"),children:[s("common.viewAll")||"View all",e.jsx(H,{size:12})]})]}),e.jsxs("div",{className:"hub-row head hub-mono",style:{gridTemplateColumns:g},children:[e.jsx("div",{children:s("server.name")}),e.jsx("div",{children:s("server.status")}),e.jsx("div",{children:s("common.type")||"Transport"}),e.jsx("div",{children:s("server.tools")}),e.jsx("div",{children:s("server.prompts")}),e.jsx("div",{children:s("nav.resources")}),e.jsx("div",{children:s("server.enabled")})]}),E.map(r=>{var i,o,N,k;return e.jsxs("div",{className:"hub-row hover cursor-pointer",style:{gridTemplateColumns:g},onClick:()=>l("/servers"),children:[e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[e.jsx("span",{className:"hub-mono truncate",style:{fontSize:13,color:r.enabled===!1?"var(--hub-ink-3)":"var(--hub-ink)"},children:r.name}),r.error&&e.jsx(y,{size:13,className:"text-[var(--hub-err)] flex-shrink-0"})]}),e.jsx("div",{className:"min-w-0",children:e.jsx(M,{status:r.status,enabled:r.enabled})}),e.jsx("div",{className:"min-w-0",children:(i=r.config)!=null&&i.type?e.jsx("span",{className:"hub-tag",title:w(s,r.config.type)??void 0,children:w(s,r.config.type)}):e.jsx("span",{style:{color:"var(--hub-ink-3)",fontSize:12},children:"—"})}),e.jsx("div",{className:"hub-num hub-mono",style:{fontSize:12.5},children:((o=r.tools)==null?void 0:o.length)||0}),e.jsx("div",{className:"hub-num hub-mono",style:{fontSize:12.5,color:"var(--hub-ink-2)"},children:((N=r.prompts)==null?void 0:N.length)||0}),e.jsx("div",{className:"hub-num hub-mono",style:{fontSize:12.5,color:"var(--hub-ink-2)"},children:((k=r.resources)==null?void 0:k.length)||0}),e.jsx("div",{className:"text-[12px]",style:{color:r.enabled!==!1?"var(--hub-ok)":"var(--hub-ink-3)"},children:r.enabled!==!1?"✓":"—"})]},r.name)})]}),e.jsxs("div",{className:"hub-card mb-5",style:{padding:16},children:[e.jsxs("div",{className:"flex justify-between items-start gap-3 mb-3",children:[e.jsxs("div",{children:[e.jsx("h3",{className:"hub-card-title",children:s("pages.dashboard.endpoints")||"MCP Endpoints"}),e.jsx("p",{className:"hub-sub",style:{marginTop:2},children:s("pages.dashboard.endpointsHint")||"Use these URLs in Claude Desktop, Cursor, or any MCP client"})]}),e.jsxs("a",{className:"hub-btn ghost",href:"https://docs.mcphub.app",target:"_blank",rel:"noopener noreferrer",style:{color:"var(--hub-ink-3)"},children:[s("common.docs")||"Docs"," →"]})]}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-2.5",children:[e.jsx(h,{label:"ALL",url:`${d}/mcp`}),e.jsx(h,{label:"SMART",url:`${d}/mcp/$smart`}),b.slice(0,2).map(r=>e.jsx(h,{label:"GROUP",url:`${d}/mcp/${r.name}`},r.id)),b.length<2&&a[0]&&e.jsx(h,{label:"SERVER",url:`${d}/mcp/${a[0].name}`})]})]})]})};export{I as default};
|
|
2
|
+
//# sourceMappingURL=Dashboard-Iq4lJkw-.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dashboard-YMaaIlIZ.js","sources":["../../src/pages/Dashboard.tsx"],"sourcesContent":["import React, { useMemo } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useNavigate } from 'react-router-dom';\nimport { RefreshCw, Plus, ChevronRight, AlertCircle } from 'lucide-react';\nimport { useServerData } from '@/hooks/useServerData';\nimport { useGroupData } from '@/hooks/useGroupData';\nimport { useSettingsData } from '@/hooks/useSettingsData';\nimport { Server } from '@/types';\nimport { EndpointCopy } from '@/components/ui/EndpointCopy';\nimport { ServerStatusDot } from '@/components/ui/StatusDot';\n\nconst Stat: React.FC<{ label: string; value: React.ReactNode; tone?: 'ok' | 'warn' | 'err' | 'muted' | 'default' }> = ({\n label,\n value,\n tone = 'default',\n}) => {\n const toneColor =\n tone === 'ok'\n ? 'oklch(0.4 0.13 145)'\n : tone === 'warn'\n ? 'oklch(0.45 0.13 80)'\n : tone === 'err'\n ? 'oklch(0.45 0.18 25)'\n : tone === 'muted'\n ? 'var(--hub-ink-3)'\n : 'var(--hub-ink)';\n return (\n <div className=\"hub-card\" style={{ padding: '14px 16px' }}>\n <div className=\"text-[12px]\" style={{ color: 'var(--hub-ink-3)' }}>\n {label}\n </div>\n <div\n className=\"hub-num\"\n style={{\n fontSize: 26,\n fontWeight: 500,\n letterSpacing: '-0.02em',\n lineHeight: 1.1,\n marginTop: 8,\n color: toneColor,\n }}\n >\n {value}\n </div>\n </div>\n );\n};\n\nconst transportLabel = (t: any, type?: string) => {\n if (!type) return null;\n if (type === 'stdio') return t('server.typeStdio') || 'stdio';\n if (type === 'sse') return t('server.typeSse') || 'sse';\n if (type === 'streamable-http') return t('server.typeStreamableHttp') || 'http';\n if (type === 'openapi') return t('server.typeOpenapi') || 'openapi';\n return type;\n};\n\nconst DashboardPage: React.FC = () => {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const { allServers, error, setError, isLoading, triggerRefresh } = useServerData({\n refreshOnMount: true,\n });\n const { groups } = useGroupData();\n const { installConfig } = useSettingsData();\n\n const [hasLoaded, setHasLoaded] = React.useState(false);\n const loadingStartedRef = React.useRef(false);\n React.useEffect(() => {\n if (isLoading) {\n loadingStartedRef.current = true;\n return;\n }\n if (loadingStartedRef.current) {\n setHasLoaded(true);\n return;\n }\n if (allServers.length > 0 || error) setHasLoaded(true);\n }, [isLoading, allServers.length, error]);\n\n const stats = useMemo(\n () => ({\n total: allServers.length,\n online: allServers.filter((s: Server) => s.status === 'connected').length,\n disabled: allServers.filter((s: Server) => s.enabled === false).length,\n offline: allServers.filter(\n (s: Server) => s.status === 'disconnected' && s.enabled !== false,\n ).length,\n connecting: allServers.filter(\n (s: Server) =>\n (s.status === 'connecting' || s.status === 'oauth_required') && s.enabled !== false,\n ).length,\n tools: allServers.reduce((acc, s) => acc + (s.tools?.length || 0), 0),\n }),\n [allServers],\n );\n\n const recentServers = useMemo(() => allServers.slice(0, 6), [allServers]);\n const baseUrl = installConfig?.baseUrl?.replace(/\\/+$/, '') || '';\n const recentServerColumns =\n 'minmax(220px,1.9fr) minmax(110px,0.95fr) minmax(120px,0.95fr) 80px 80px 90px 72px';\n\n const showSkeleton = !hasLoaded;\n\n return (\n <div>\n {/* Header */}\n <div className=\"flex items-end justify-between gap-4 mb-6\">\n <div>\n <h1 className=\"hub-h1\">{t('pages.dashboard.title')}</h1>\n <p className=\"hub-sub\">\n {t('pages.dashboard.totalServers')} · <span className=\"hub-num\">{stats.total}</span>\n {' · '}\n {t('pages.dashboard.onlineServers')} · <span className=\"hub-num\">{stats.online}</span>\n </p>\n </div>\n <div className=\"flex gap-2\">\n <button className=\"hub-btn\" onClick={() => triggerRefresh()}>\n <RefreshCw size={13} /> {t('common.refresh')}\n </button>\n <button className=\"hub-btn primary\" onClick={() => navigate('/servers')}>\n <Plus size={13} /> {t('server.add')}\n </button>\n </div>\n </div>\n\n {error && (\n <div\n className=\"hub-card flex items-center justify-between gap-3 mb-5\"\n style={{\n padding: '10px 14px',\n borderColor: 'oklch(0.85 0.1 25)',\n background: 'oklch(0.97 0.03 25)',\n color: 'oklch(0.4 0.18 25)',\n }}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n <AlertCircle size={14} className=\"flex-shrink-0\" />\n <span className=\"truncate text-[13px]\">{error}</span>\n </div>\n <button\n className=\"hub-icon-btn sm\"\n onClick={() => setError(null)}\n aria-label={t('app.closeButton')}\n >\n ✕\n </button>\n </div>\n )}\n\n {/* Stat row */}\n {showSkeleton ? (\n <div className=\"grid grid-cols-2 md:grid-cols-5 gap-3 mb-6\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div\n key={i}\n className=\"hub-card animate-pulse\"\n style={{ padding: '14px 16px', height: 78 }}\n />\n ))}\n </div>\n ) : (\n <div className=\"grid grid-cols-2 md:grid-cols-5 gap-3 mb-6\">\n <Stat label={t('pages.dashboard.totalServers')} value={stats.total} />\n <Stat label={t('pages.dashboard.onlineServers')} value={stats.online} tone=\"ok\" />\n <Stat label={t('pages.dashboard.connectingServers')} value={stats.connecting} tone=\"warn\" />\n <Stat label={t('pages.dashboard.offlineServers')} value={stats.offline} tone=\"err\" />\n <Stat label={t('pages.dashboard.disabledServers')} value={stats.disabled} tone=\"muted\" />\n </div>\n )}\n\n {/* Recent servers */}\n {allServers.length > 0 && !showSkeleton && (\n <div className=\"hub-card overflow-hidden mb-6\">\n <div\n className=\"flex items-center justify-between px-4 py-3\"\n style={{ borderBottom: '1px solid var(--hub-line-2)' }}\n >\n <h3 className=\"hub-card-title\">{t('pages.dashboard.recentServers')}</h3>\n <button\n className=\"hub-btn ghost sm\"\n style={{ color: 'var(--hub-ink-3)' }}\n onClick={() => navigate('/servers')}\n >\n {t('common.viewAll') || 'View all'}\n <ChevronRight size={12} />\n </button>\n </div>\n <div\n className=\"hub-row head hub-mono\"\n style={{ gridTemplateColumns: recentServerColumns }}\n >\n <div>{t('server.name')}</div>\n <div>{t('server.status')}</div>\n <div>{t('common.type') || 'Transport'}</div>\n <div>{t('server.tools')}</div>\n <div>{t('server.prompts')}</div>\n <div>{t('nav.resources')}</div>\n <div>{t('server.enabled')}</div>\n </div>\n {recentServers.map((s) => (\n <div\n key={s.name}\n className=\"hub-row hover cursor-pointer\"\n style={{ gridTemplateColumns: recentServerColumns }}\n onClick={() => navigate('/servers')}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n <span\n className=\"hub-mono truncate\"\n style={{ fontSize: 13, color: s.enabled === false ? 'var(--hub-ink-3)' : 'var(--hub-ink)' }}\n >\n {s.name}\n </span>\n {s.error && <AlertCircle size={13} className=\"text-[var(--hub-err)] flex-shrink-0\" />}\n </div>\n <div className=\"min-w-0\">\n <ServerStatusDot status={s.status} enabled={s.enabled} />\n </div>\n <div className=\"min-w-0\">\n {s.config?.type ? (\n <span className=\"hub-tag\" title={transportLabel(t, s.config.type) ?? undefined}>\n {transportLabel(t, s.config.type)}\n </span>\n ) : (\n <span style={{ color: 'var(--hub-ink-3)', fontSize: 12 }}>—</span>\n )}\n </div>\n <div className=\"hub-num hub-mono\" style={{ fontSize: 12.5 }}>\n {s.tools?.length || 0}\n </div>\n <div className=\"hub-num hub-mono\" style={{ fontSize: 12.5, color: 'var(--hub-ink-2)' }}>\n {s.prompts?.length || 0}\n </div>\n <div className=\"hub-num hub-mono\" style={{ fontSize: 12.5, color: 'var(--hub-ink-2)' }}>\n {s.resources?.length || 0}\n </div>\n <div className=\"text-[12px]\" style={{ color: s.enabled !== false ? 'var(--hub-ok)' : 'var(--hub-ink-3)' }}>\n {s.enabled !== false ? '✓' : '—'}\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Endpoint quick-access */}\n <div className=\"hub-card mb-5\" style={{ padding: 16 }}>\n <div className=\"flex justify-between items-start gap-3 mb-3\">\n <div>\n <h3 className=\"hub-card-title\">{t('pages.dashboard.endpoints') || 'MCP Endpoints'}</h3>\n <p className=\"hub-sub\" style={{ marginTop: 2 }}>\n {t('pages.dashboard.endpointsHint') ||\n 'Use these URLs in Claude Desktop, Cursor, or any MCP client'}\n </p>\n </div>\n <a\n className=\"hub-btn ghost\"\n href=\"https://docs.mcphub.app\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style={{ color: 'var(--hub-ink-3)' }}\n >\n {t('common.docs') || 'Docs'} →\n </a>\n </div>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-2.5\">\n <EndpointCopy label=\"ALL\" url={`${baseUrl}/mcp`} />\n <EndpointCopy label=\"SMART\" url={`${baseUrl}/mcp/$smart`} />\n {groups.slice(0, 2).map((g) => (\n <EndpointCopy key={g.id} label=\"GROUP\" url={`${baseUrl}/mcp/${g.name}`} />\n ))}\n {/* Pad with first server endpoint if there's space */}\n {groups.length < 2 && allServers[0] && (\n <EndpointCopy label=\"SERVER\" url={`${baseUrl}/mcp/${allServers[0].name}`} />\n )}\n </div>\n </div>\n </div>\n );\n};\n\nexport default DashboardPage;\n"],"names":["Stat","label","value","tone","toneColor","jsxs","jsx","transportLabel","t","type","DashboardPage","useTranslation","navigate","useNavigate","allServers","error","setError","isLoading","triggerRefresh","useServerData","groups","useGroupData","installConfig","useSettingsData","hasLoaded","setHasLoaded","React","loadingStartedRef","stats","useMemo","s","acc","_a","recentServers","baseUrl","recentServerColumns","showSkeleton","RefreshCw","Plus","AlertCircle","_","ChevronRight","ServerStatusDot","_b","_c","_d","EndpointCopy","g"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Dashboard-Iq4lJkw-.js","sources":["../../src/pages/Dashboard.tsx"],"sourcesContent":["import React, { useMemo } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useNavigate } from 'react-router-dom';\nimport { RefreshCw, Plus, ChevronRight, AlertCircle } from 'lucide-react';\nimport { useServerData } from '@/hooks/useServerData';\nimport { useGroupData } from '@/hooks/useGroupData';\nimport { useSettingsData } from '@/hooks/useSettingsData';\nimport { Server } from '@/types';\nimport { EndpointCopy } from '@/components/ui/EndpointCopy';\nimport { ServerStatusDot } from '@/components/ui/StatusDot';\n\nconst Stat: React.FC<{ label: string; value: React.ReactNode; tone?: 'ok' | 'warn' | 'err' | 'muted' | 'default' }> = ({\n label,\n value,\n tone = 'default',\n}) => {\n const toneColor =\n tone === 'ok'\n ? 'oklch(0.4 0.13 145)'\n : tone === 'warn'\n ? 'oklch(0.45 0.13 80)'\n : tone === 'err'\n ? 'oklch(0.45 0.18 25)'\n : tone === 'muted'\n ? 'var(--hub-ink-3)'\n : 'var(--hub-ink)';\n return (\n <div className=\"hub-card\" style={{ padding: '14px 16px' }}>\n <div className=\"text-[12px]\" style={{ color: 'var(--hub-ink-3)' }}>\n {label}\n </div>\n <div\n className=\"hub-num\"\n style={{\n fontSize: 26,\n fontWeight: 500,\n letterSpacing: '-0.02em',\n lineHeight: 1.1,\n marginTop: 8,\n color: toneColor,\n }}\n >\n {value}\n </div>\n </div>\n );\n};\n\nconst transportLabel = (t: any, type?: string) => {\n if (!type) return null;\n if (type === 'stdio') return t('server.typeStdio') || 'stdio';\n if (type === 'sse') return t('server.typeSse') || 'sse';\n if (type === 'streamable-http') return t('server.typeStreamableHttp') || 'http';\n if (type === 'openapi') return t('server.typeOpenapi') || 'openapi';\n return type;\n};\n\nconst DashboardPage: React.FC = () => {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const { allServers, error, setError, isLoading, triggerRefresh } = useServerData({\n refreshOnMount: true,\n });\n const { groups } = useGroupData();\n const { installConfig } = useSettingsData();\n\n const [hasLoaded, setHasLoaded] = React.useState(false);\n const loadingStartedRef = React.useRef(false);\n React.useEffect(() => {\n if (isLoading) {\n loadingStartedRef.current = true;\n return;\n }\n if (loadingStartedRef.current) {\n setHasLoaded(true);\n return;\n }\n if (allServers.length > 0 || error) setHasLoaded(true);\n }, [isLoading, allServers.length, error]);\n\n const stats = useMemo(\n () => ({\n total: allServers.length,\n online: allServers.filter((s: Server) => s.status === 'connected').length,\n disabled: allServers.filter((s: Server) => s.enabled === false).length,\n offline: allServers.filter(\n (s: Server) => s.status === 'disconnected' && s.enabled !== false,\n ).length,\n connecting: allServers.filter(\n (s: Server) =>\n (s.status === 'connecting' || s.status === 'oauth_required') && s.enabled !== false,\n ).length,\n tools: allServers.reduce((acc, s) => acc + (s.tools?.length || 0), 0),\n }),\n [allServers],\n );\n\n const recentServers = useMemo(() => allServers.slice(0, 6), [allServers]);\n const baseUrl = installConfig?.baseUrl?.replace(/\\/+$/, '') || '';\n const recentServerColumns =\n 'minmax(220px,1.9fr) minmax(110px,0.95fr) minmax(120px,0.95fr) 80px 80px 90px 72px';\n\n const showSkeleton = !hasLoaded;\n\n return (\n <div>\n {/* Header */}\n <div className=\"flex items-end justify-between gap-4 mb-6\">\n <div>\n <h1 className=\"hub-h1\">{t('pages.dashboard.title')}</h1>\n <p className=\"hub-sub\">\n {t('pages.dashboard.totalServers')} · <span className=\"hub-num\">{stats.total}</span>\n {' · '}\n {t('pages.dashboard.onlineServers')} · <span className=\"hub-num\">{stats.online}</span>\n </p>\n </div>\n <div className=\"flex gap-2\">\n <button className=\"hub-btn\" onClick={() => triggerRefresh()}>\n <RefreshCw size={13} /> {t('common.refresh')}\n </button>\n <button className=\"hub-btn primary\" onClick={() => navigate('/servers')}>\n <Plus size={13} /> {t('server.add')}\n </button>\n </div>\n </div>\n\n {error && (\n <div\n className=\"hub-card flex items-center justify-between gap-3 mb-5\"\n style={{\n padding: '10px 14px',\n borderColor: 'oklch(0.85 0.1 25)',\n background: 'oklch(0.97 0.03 25)',\n color: 'oklch(0.4 0.18 25)',\n }}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n <AlertCircle size={14} className=\"flex-shrink-0\" />\n <span className=\"truncate text-[13px]\">{error}</span>\n </div>\n <button\n className=\"hub-icon-btn sm\"\n onClick={() => setError(null)}\n aria-label={t('app.closeButton')}\n >\n ✕\n </button>\n </div>\n )}\n\n {/* Stat row */}\n {showSkeleton ? (\n <div className=\"grid grid-cols-2 md:grid-cols-5 gap-3 mb-6\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div\n key={i}\n className=\"hub-card animate-pulse\"\n style={{ padding: '14px 16px', height: 78 }}\n />\n ))}\n </div>\n ) : (\n <div className=\"grid grid-cols-2 md:grid-cols-5 gap-3 mb-6\">\n <Stat label={t('pages.dashboard.totalServers')} value={stats.total} />\n <Stat label={t('pages.dashboard.onlineServers')} value={stats.online} tone=\"ok\" />\n <Stat label={t('pages.dashboard.connectingServers')} value={stats.connecting} tone=\"warn\" />\n <Stat label={t('pages.dashboard.offlineServers')} value={stats.offline} tone=\"err\" />\n <Stat label={t('pages.dashboard.disabledServers')} value={stats.disabled} tone=\"muted\" />\n </div>\n )}\n\n {/* Recent servers */}\n {allServers.length > 0 && !showSkeleton && (\n <div className=\"hub-card overflow-hidden mb-6\">\n <div\n className=\"flex items-center justify-between px-4 py-3\"\n style={{ borderBottom: '1px solid var(--hub-line-2)' }}\n >\n <h3 className=\"hub-card-title\">{t('pages.dashboard.recentServers')}</h3>\n <button\n className=\"hub-btn ghost sm\"\n style={{ color: 'var(--hub-ink-3)' }}\n onClick={() => navigate('/servers')}\n >\n {t('common.viewAll') || 'View all'}\n <ChevronRight size={12} />\n </button>\n </div>\n <div\n className=\"hub-row head hub-mono\"\n style={{ gridTemplateColumns: recentServerColumns }}\n >\n <div>{t('server.name')}</div>\n <div>{t('server.status')}</div>\n <div>{t('common.type') || 'Transport'}</div>\n <div>{t('server.tools')}</div>\n <div>{t('server.prompts')}</div>\n <div>{t('nav.resources')}</div>\n <div>{t('server.enabled')}</div>\n </div>\n {recentServers.map((s) => (\n <div\n key={s.name}\n className=\"hub-row hover cursor-pointer\"\n style={{ gridTemplateColumns: recentServerColumns }}\n onClick={() => navigate('/servers')}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n <span\n className=\"hub-mono truncate\"\n style={{ fontSize: 13, color: s.enabled === false ? 'var(--hub-ink-3)' : 'var(--hub-ink)' }}\n >\n {s.name}\n </span>\n {s.error && <AlertCircle size={13} className=\"text-[var(--hub-err)] flex-shrink-0\" />}\n </div>\n <div className=\"min-w-0\">\n <ServerStatusDot status={s.status} enabled={s.enabled} />\n </div>\n <div className=\"min-w-0\">\n {s.config?.type ? (\n <span className=\"hub-tag\" title={transportLabel(t, s.config.type) ?? undefined}>\n {transportLabel(t, s.config.type)}\n </span>\n ) : (\n <span style={{ color: 'var(--hub-ink-3)', fontSize: 12 }}>—</span>\n )}\n </div>\n <div className=\"hub-num hub-mono\" style={{ fontSize: 12.5 }}>\n {s.tools?.length || 0}\n </div>\n <div className=\"hub-num hub-mono\" style={{ fontSize: 12.5, color: 'var(--hub-ink-2)' }}>\n {s.prompts?.length || 0}\n </div>\n <div className=\"hub-num hub-mono\" style={{ fontSize: 12.5, color: 'var(--hub-ink-2)' }}>\n {s.resources?.length || 0}\n </div>\n <div className=\"text-[12px]\" style={{ color: s.enabled !== false ? 'var(--hub-ok)' : 'var(--hub-ink-3)' }}>\n {s.enabled !== false ? '✓' : '—'}\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Endpoint quick-access */}\n <div className=\"hub-card mb-5\" style={{ padding: 16 }}>\n <div className=\"flex justify-between items-start gap-3 mb-3\">\n <div>\n <h3 className=\"hub-card-title\">{t('pages.dashboard.endpoints') || 'MCP Endpoints'}</h3>\n <p className=\"hub-sub\" style={{ marginTop: 2 }}>\n {t('pages.dashboard.endpointsHint') ||\n 'Use these URLs in Claude Desktop, Cursor, or any MCP client'}\n </p>\n </div>\n <a\n className=\"hub-btn ghost\"\n href=\"https://docs.mcphub.app\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style={{ color: 'var(--hub-ink-3)' }}\n >\n {t('common.docs') || 'Docs'} →\n </a>\n </div>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-2.5\">\n <EndpointCopy label=\"ALL\" url={`${baseUrl}/mcp`} />\n <EndpointCopy label=\"SMART\" url={`${baseUrl}/mcp/$smart`} />\n {groups.slice(0, 2).map((g) => (\n <EndpointCopy key={g.id} label=\"GROUP\" url={`${baseUrl}/mcp/${g.name}`} />\n ))}\n {/* Pad with first server endpoint if there's space */}\n {groups.length < 2 && allServers[0] && (\n <EndpointCopy label=\"SERVER\" url={`${baseUrl}/mcp/${allServers[0].name}`} />\n )}\n </div>\n </div>\n </div>\n );\n};\n\nexport default DashboardPage;\n"],"names":["Stat","label","value","tone","toneColor","jsxs","jsx","transportLabel","t","type","DashboardPage","useTranslation","navigate","useNavigate","allServers","error","setError","isLoading","triggerRefresh","useServerData","groups","useGroupData","installConfig","useSettingsData","hasLoaded","setHasLoaded","React","loadingStartedRef","stats","useMemo","s","acc","_a","recentServers","baseUrl","recentServerColumns","showSkeleton","RefreshCw","Plus","AlertCircle","_","ChevronRight","ServerStatusDot","_b","_c","_d","EndpointCopy","g"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as y,j as t}from"./framework-vendor-BUhDPOUZ.js";import{i as x,m as r}from"./index-
|
|
2
|
-
//# sourceMappingURL=EndpointCopy-
|
|
1
|
+
import{r as y,j as t}from"./framework-vendor-BUhDPOUZ.js";import{i as x,m as r}from"./index-9Vmc8uCD.js";import{u as h}from"./i18n-vendor-Kbr87Ofu.js";import{C,o as b}from"./icons-vendor-CKgJB3SC.js";const f=async e=>{try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(e),!0}catch{}try{const o=document.createElement("textarea");o.value=e,o.style.position="fixed",o.style.left="-9999px",document.body.appendChild(o),o.focus(),o.select();const s=document.execCommand("copy");return document.body.removeChild(o),s}catch{return!1}},E=({url:e,label:o,prefix:s,className:p,copyValue:d,ariaLabel:l})=>{const{t:c}=h(),{showToast:n}=x(),[a,i]=y.useState(!1),m=async u=>{if(u.stopPropagation(),!await f(d??e)){n(c("common.copyFailed")||"Copy failed","error");return}i(!0),n(c("common.copySuccess")||"Copied to clipboard","success"),setTimeout(()=>i(!1),1200)};return t.jsxs("div",{className:r("hub-endpoint",p),role:"group","aria-label":l||e,children:[o&&t.jsx("div",{className:"hub-endpoint-label",children:o}),t.jsxs("div",{className:"hub-endpoint-url",title:e,children:[s&&t.jsx("span",{style:{color:"var(--hub-ink-3)"},children:s}),e]}),t.jsx("button",{type:"button",onClick:m,className:r("hub-endpoint-copy",a?"copied":""),title:c("common.copy")||"Copy","aria-label":c("common.copy")||"Copy",children:a?t.jsx(C,{size:13}):t.jsx(b,{size:13})})]})};export{E};
|
|
2
|
+
//# sourceMappingURL=EndpointCopy-BSQygOcu.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EndpointCopy-
|
|
1
|
+
{"version":3,"file":"EndpointCopy-BSQygOcu.js","sources":["../../src/components/ui/EndpointCopy.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { Copy, Check } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { useToast } from '@/contexts/ToastContext';\nimport { cn } from '@/utils/cn';\n\nconst copyText = async (value: string): Promise<boolean> => {\n try {\n if (navigator.clipboard && window.isSecureContext) {\n await navigator.clipboard.writeText(value);\n return true;\n }\n } catch {\n // fall through to fallback\n }\n try {\n const el = document.createElement('textarea');\n el.value = value;\n el.style.position = 'fixed';\n el.style.left = '-9999px';\n document.body.appendChild(el);\n el.focus();\n el.select();\n const ok = document.execCommand('copy');\n document.body.removeChild(el);\n return ok;\n } catch {\n return false;\n }\n};\n\ninterface EndpointCopyProps {\n url: string;\n label?: string;\n prefix?: string;\n className?: string;\n /** Optionally override the value placed on the clipboard. */\n copyValue?: string;\n ariaLabel?: string;\n}\n\nexport const EndpointCopy: React.FC<EndpointCopyProps> = ({\n url,\n label,\n prefix,\n className,\n copyValue,\n ariaLabel,\n}) => {\n const { t } = useTranslation();\n const { showToast } = useToast();\n const [copied, setCopied] = useState(false);\n\n const onCopy = async (e: React.MouseEvent) => {\n e.stopPropagation();\n const ok = await copyText(copyValue ?? url);\n if (!ok) {\n showToast(t('common.copyFailed') || 'Copy failed', 'error');\n return;\n }\n setCopied(true);\n showToast(t('common.copySuccess') || 'Copied to clipboard', 'success');\n setTimeout(() => setCopied(false), 1200);\n };\n\n return (\n <div className={cn('hub-endpoint', className)} role=\"group\" aria-label={ariaLabel || url}>\n {label && <div className=\"hub-endpoint-label\">{label}</div>}\n <div className=\"hub-endpoint-url\" title={url}>\n {prefix && <span style={{ color: 'var(--hub-ink-3)' }}>{prefix}</span>}\n {url}\n </div>\n <button\n type=\"button\"\n onClick={onCopy}\n className={cn('hub-endpoint-copy', copied ? 'copied' : '')}\n title={t('common.copy') || 'Copy'}\n aria-label={t('common.copy') || 'Copy'}\n >\n {copied ? <Check size={13} /> : <Copy size={13} />}\n </button>\n </div>\n );\n};\n\ninterface MonoCopyProps {\n text: string;\n className?: string;\n copyValue?: string;\n title?: string;\n}\n\n/** Inline monospace value with hover-to-copy icon. */\nexport const MonoCopy: React.FC<MonoCopyProps> = ({ text, className, copyValue, title }) => {\n const { t } = useTranslation();\n const { showToast } = useToast();\n const [copied, setCopied] = useState(false);\n\n const onCopy = async (e: React.MouseEvent) => {\n e.stopPropagation();\n const ok = await copyText(copyValue ?? text);\n if (!ok) {\n showToast(t('common.copyFailed') || 'Copy failed', 'error');\n return;\n }\n setCopied(true);\n showToast(t('common.copySuccess') || 'Copied to clipboard', 'success');\n setTimeout(() => setCopied(false), 1200);\n };\n\n return (\n <span\n className={cn(\n 'hub-mono inline-flex items-center gap-1.5 group cursor-pointer text-[12.5px]',\n className,\n )}\n onClick={onCopy}\n title={title || text}\n role=\"button\"\n >\n <span className=\"truncate\">{text}</span>\n {copied ? (\n <Check size={12} className=\"text-[var(--hub-ok)] flex-shrink-0\" />\n ) : (\n <Copy\n size={12}\n className=\"text-[var(--hub-ink-3)] opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0\"\n />\n )}\n </span>\n );\n};\n"],"names":["copyText","value","el","ok","EndpointCopy","url","label","prefix","className","copyValue","ariaLabel","t","useTranslation","showToast","useToast","copied","setCopied","useState","onCopy","e","jsxs","cn","jsx","Check","Copy"],"mappings":"wMAMA,MAAMA,EAAW,MAAOC,GAAoC,CAC1D,GAAI,CACF,GAAI,UAAU,WAAa,OAAO,gBAChC,aAAM,UAAU,UAAU,UAAUA,CAAK,EAClC,EAEX,MAAQ,CAER,CACA,GAAI,CACF,MAAMC,EAAK,SAAS,cAAc,UAAU,EAC5CA,EAAG,MAAQD,EACXC,EAAG,MAAM,SAAW,QACpBA,EAAG,MAAM,KAAO,UAChB,SAAS,KAAK,YAAYA,CAAE,EAC5BA,EAAG,MAAA,EACHA,EAAG,OAAA,EACH,MAAMC,EAAK,SAAS,YAAY,MAAM,EACtC,gBAAS,KAAK,YAAYD,CAAE,EACrBC,CACT,MAAQ,CACN,MAAO,EACT,CACF,EAYaC,EAA4C,CAAC,CACxD,IAAAC,EACA,MAAAC,EACA,OAAAC,EACA,UAAAC,EACA,UAAAC,EACA,UAAAC,CACF,IAAM,CACJ,KAAM,CAAE,EAAAC,CAAA,EAAMC,EAAA,EACR,CAAE,UAAAC,CAAA,EAAcC,EAAA,EAChB,CAACC,EAAQC,CAAS,EAAIC,EAAAA,SAAS,EAAK,EAEpCC,EAAS,MAAOC,GAAwB,CAG5C,GAFAA,EAAE,gBAAA,EAEE,CADO,MAAMnB,EAASS,GAAaJ,CAAG,EACjC,CACPQ,EAAUF,EAAE,mBAAmB,GAAK,cAAe,OAAO,EAC1D,MACF,CACAK,EAAU,EAAI,EACdH,EAAUF,EAAE,oBAAoB,GAAK,sBAAuB,SAAS,EACrE,WAAW,IAAMK,EAAU,EAAK,EAAG,IAAI,CACzC,EAEA,OACEI,EAAAA,KAAC,MAAA,CAAI,UAAWC,EAAG,eAAgBb,CAAS,EAAG,KAAK,QAAQ,aAAYE,GAAaL,EAClF,SAAA,CAAAC,GAASgB,EAAAA,IAAC,MAAA,CAAI,UAAU,qBAAsB,SAAAhB,EAAM,EACrDc,EAAAA,KAAC,MAAA,CAAI,UAAU,mBAAmB,MAAOf,EACtC,SAAA,CAAAE,SAAW,OAAA,CAAK,MAAO,CAAE,MAAO,kBAAA,EAAuB,SAAAA,EAAO,EAC9DF,CAAA,EACH,EACAiB,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,QAASJ,EACT,UAAWG,EAAG,oBAAqBN,EAAS,SAAW,EAAE,EACzD,MAAOJ,EAAE,aAAa,GAAK,OAC3B,aAAYA,EAAE,aAAa,GAAK,OAE/B,SAAAI,QAAUQ,EAAA,CAAM,KAAM,GAAI,EAAKD,EAAAA,IAACE,EAAA,CAAK,KAAM,EAAA,CAAI,CAAA,CAAA,CAClD,EACF,CAEJ"}
|