diff --git a/.env b/.env index f45a4bc..cf6ed3e 100644 --- a/.env +++ b/.env @@ -3,7 +3,7 @@ # add CRATES_PRO_HOST, CRATES_PRO_INTERNAL_HOST to your enviroment for development -CRATES_PRO_HOST=$CRATES_PRO_HOST -CRATES_PRO_INTERNAL_HOST=$CRATES_PRO_INTERNAL_HOST +CRATES_PRO_HOST=http://210.28.134.203:6888 +CRATES_PRO_INTERNAL_HOST=http://210.28.134.203:6888 SECRET_KEY=$YOUR_SECRET_KEY #(not prefixed with NEXT_PUBLIC_ ) \ No newline at end of file diff --git a/app/[nsfront]/[nsbehind]/[name]/[version]/dependencies/graph/page.tsx b/app/[nsfront]/[nsbehind]/[name]/[version]/dependencies/graph/page.tsx index cae7841..4b9e5f2 100644 --- a/app/[nsfront]/[nsbehind]/[name]/[version]/dependencies/graph/page.tsx +++ b/app/[nsfront]/[nsbehind]/[name]/[version]/dependencies/graph/page.tsx @@ -48,7 +48,8 @@ const CratePage = () => { if (error) return
Error: {error}
; return ( -
+
+ diff --git a/app/[nsfront]/[nsbehind]/[name]/[version]/versions/page.tsx b/app/[nsfront]/[nsbehind]/[name]/[version]/versions/page.tsx new file mode 100644 index 0000000..bfacd59 --- /dev/null +++ b/app/[nsfront]/[nsbehind]/[name]/[version]/versions/page.tsx @@ -0,0 +1,58 @@ +//Dependents页面 +"use client"; +import React, { useEffect, useState } from 'react'; + +import VersionsTable from '@/components/VersionsTable'; +import { cratesInfo } from '@/app/lib/all_interface'; +import { useParams } from 'next/navigation'; + + + +const CratePage = () => { + const [results, setResults] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const params = useParams(); + + + + useEffect(() => { + const fetchCrateData = async () => { + try { + const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}`); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + + + setResults(data); // 设置获取的数据 + + } catch (error) { + setError(null); + console.log('Error fetching data:', error); + } finally { + setLoading(false); // 完成加载 + } + }; + fetchCrateData(); // 调用函数来获取数据 + }, [params.name, params.version, params.nsfront, params.nsbehind]); // 依赖项数组,确保在 crateName 或 version 改变时重新获取数据 + + if (loading) return
Loading...
; + if (error) return
Error: {error}
; + + + + return ( +
+ + + +
+ ); +}; + +export default CratePage; \ No newline at end of file diff --git a/app/api/search/route.tsx b/app/api/search/route.tsx index eb7c4fb..be6fea1 100644 --- a/app/api/search/route.tsx +++ b/app/api/search/route.tsx @@ -2,7 +2,6 @@ import { NextResponse } from 'next/server'; export async function POST(request: Request) { const endpoint = process.env.CRATES_PRO_INTERNAL_HOST; - const apiUrl = `${endpoint}/api/search`; const requestBody = await request.json(); console.log("Request Body:", requestBody); diff --git a/components/DependencyGraph.tsx b/components/DependencyGraph.tsx index fee2b92..a265e06 100644 --- a/components/DependencyGraph.tsx +++ b/components/DependencyGraph.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useRef, useState } from 'react'; import * as d3 from 'd3'; -// import { useParams } from 'next/navigation'; export interface GraphDependency { crate_name: string; @@ -26,7 +25,6 @@ export interface DependencyGraphProps { const DependencyGraph: React.FC = ({ crateName, currentVersion }) => { const [graphDependencies, setGraphDependencies] = useState(null); const d3Container = useRef(null); - // const params = useParams(); useEffect(() => { async function fetchDependencyTree(name: string, version: string, visited: Set): Promise { @@ -112,10 +110,11 @@ const DependencyGraph: React.FC = ({ crateName, currentVer const nodes = Array.from(nodesMap.values()); + // Remove the forceCenter to allow free movement of nodes const simulation = d3.forceSimulation(nodes) .force('link', d3.forceLink(links).id(d => d.id).distance(100)) .force('charge', d3.forceManyBody().strength(-500)) - .force('center', d3.forceCenter(width / 2, height / 2)) + // Remove center force to prevent auto centering .force('collide', d3.forceCollide().radius(50)); const link = svg.append('g') @@ -191,11 +190,31 @@ const DependencyGraph: React.FC = ({ crateName, currentVer d.fy = null; } + // Zoom functionality + // const zoom = d3.zoom() + // .scaleExtent([0.1, 10]) // Minimum and maximum zoom scales + // .on('zoom', (event) => { + // svg.selectAll('g') + // .attr('transform', event.transform); + // }); + + // svg.call(zoom as any); + + // }, [graphDependencies]); + const zoom = d3.zoom() + .scaleExtent([0.1, 10]) // Minimum and maximum zoom scales + .on('zoom', (event) => { + svg.selectAll('g') + .attr('transform', event.transform); + }); + + svg.call(zoom); // 不再使用 any + }, [graphDependencies]); return ( -
-
+
+
); }; diff --git a/components/HeaderWithSearch.tsx b/components/HeaderWithSearch.tsx index 9070e76..057fbe5 100644 --- a/components/HeaderWithSearch.tsx +++ b/components/HeaderWithSearch.tsx @@ -27,6 +27,7 @@ const Header = () => { { name: 'Overview', path: '' }, { name: 'Dependencies', path: '/dependencies' }, { name: 'Dependents', path: '/dependents' }, + { name: 'Versions', path: '/versions' }, ]; diff --git a/components/VersionsTable.tsx b/components/VersionsTable.tsx new file mode 100644 index 0000000..b48af74 --- /dev/null +++ b/components/VersionsTable.tsx @@ -0,0 +1,108 @@ +'use client'; +import React, { useEffect, useState, useCallback } from 'react'; +import { Table } from 'antd'; +import { useParams } from 'next/navigation'; + +// 假设后端接口返回的类型 +interface VersionInfo { + version: string; + publishDay: string; + dependentsNumber: number; +} + +interface VersionsTableProps { + data: string[] | undefined; // 传入的版本号数组 +} + +const VersionsTable: React.FC = ({ data }) => { + const [versionsData, setVersionsData] = useState([]); + const [loading, setLoading] = useState(false); + const params = useParams(); + + // 获取版本发布日的函数 + const fetchPublishDay = useCallback(async (version: string) => { + try { + const response = await fetch(`/api/publish-day/${version}`); + if (!response.ok) { + throw new Error('Failed to fetch publish day'); + } + const data = await response.json(); + return data.publishDay || 'N/A'; // 如果没有返回值,则默认返回 'N/A' + } catch (error) { + console.error('Error fetching publish day:', error); + return 'N/A'; // 请求失败时返回默认值 + } + }, []); + + // 获取版本依赖数的函数 + const fetchDependentsNumber = useCallback(async (version: string) => { + try { + const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${version}/dependents`); + if (!response.ok) { + throw new Error('Failed to fetch dependents number'); + } + const data = await response.json(); + return data.direct_count + data.indirect_count || 0; // 如果没有返回值,则默认返回 0 + } catch (error) { + console.error('Error fetching dependents number:', error); + return 0; // 请求失败时返回默认值 + } + }, [params.nsfront, params.nsbehind, params.name]); + + // 请求版本的发布日和依赖数 + const fetchVersionDetails = useCallback(async (version: string) => { + const publishDay = await fetchPublishDay(version); + const dependentsNumber = await fetchDependentsNumber(version); + return { version, publishDay, dependentsNumber }; + }, [fetchPublishDay, fetchDependentsNumber]); + + useEffect(() => { + const fetchData = async () => { + if (data && data.length > 0) { + setLoading(true); + const versionDetails = await Promise.all( + data.map(async (version) => { + return await fetchVersionDetails(version); + }) + ); + setVersionsData(versionDetails); + setLoading(false); + } + }; + + fetchData(); + }, [data, fetchVersionDetails]); + + const columns = [ + { + title: 'Version', + dataIndex: 'version', + key: 'version', + render: (text: string) => {text}, + }, + { + title: 'Publish Day', + dataIndex: 'publishDay', + key: 'publishDay', + render: (text: string) => {text}, + }, + { + title: 'Dependents Number', + dataIndex: 'dependentsNumber', + key: 'dependentsNumber', + render: (text: number) => {text}, + }, + ]; + + return ( + + ); +}; + +export default VersionsTable; \ No newline at end of file