Skip to content

Commit ad52056

Browse files
authored
versions page (#97)
* aaa * solved some bugs * dependencies graph * version-page
1 parent d3bbe84 commit ad52056

File tree

7 files changed

+195
-9
lines changed

7 files changed

+195
-9
lines changed

.env

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44

55
# add CRATES_PRO_HOST, CRATES_PRO_INTERNAL_HOST to your enviroment for development
6-
CRATES_PRO_HOST=$CRATES_PRO_HOST
7-
CRATES_PRO_INTERNAL_HOST=$CRATES_PRO_INTERNAL_HOST
6+
CRATES_PRO_HOST=http://210.28.134.203:6888
7+
CRATES_PRO_INTERNAL_HOST=http://210.28.134.203:6888
88

99
SECRET_KEY=$YOUR_SECRET_KEY #(not prefixed with NEXT_PUBLIC_ )

app/[nsfront]/[nsbehind]/[name]/[version]/dependencies/graph/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ const CratePage = () => {
4848
if (error) return <div className="text-red-500">Error: {error}</div>;
4949

5050
return (
51-
<div className="container mx-auto p-4">
51+
<div className="w-full h-full p-6">
52+
5253

5354

5455
<DependencyGraph crateName={params.name as string} currentVersion={params.version as string} />
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//Dependents页面
2+
"use client";
3+
import React, { useEffect, useState } from 'react';
4+
5+
import VersionsTable from '@/components/VersionsTable';
6+
import { cratesInfo } from '@/app/lib/all_interface';
7+
import { useParams } from 'next/navigation';
8+
9+
10+
11+
const CratePage = () => {
12+
const [results, setResults] = useState<cratesInfo | null>(null);
13+
const [loading, setLoading] = useState(true);
14+
const [error, setError] = useState(null);
15+
16+
const params = useParams();
17+
18+
19+
20+
useEffect(() => {
21+
const fetchCrateData = async () => {
22+
try {
23+
const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}`);
24+
25+
if (!response.ok) {
26+
throw new Error(`HTTP error! status: ${response.status}`);
27+
}
28+
29+
const data = await response.json();
30+
31+
32+
setResults(data); // 设置获取的数据
33+
34+
} catch (error) {
35+
setError(null);
36+
console.log('Error fetching data:', error);
37+
} finally {
38+
setLoading(false); // 完成加载
39+
}
40+
};
41+
fetchCrateData(); // 调用函数来获取数据
42+
}, [params.name, params.version, params.nsfront, params.nsbehind]); // 依赖项数组,确保在 crateName 或 version 改变时重新获取数据
43+
44+
if (loading) return <div>Loading...</div>;
45+
if (error) return <div className="text-red-500">Error: {error}</div>;
46+
47+
48+
49+
return (
50+
<div>
51+
52+
<VersionsTable data={results?.versions} />
53+
54+
</div>
55+
);
56+
};
57+
58+
export default CratePage;

app/api/search/route.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { NextResponse } from 'next/server';
22

33
export async function POST(request: Request) {
44
const endpoint = process.env.CRATES_PRO_INTERNAL_HOST;
5-
65
const apiUrl = `${endpoint}/api/search`;
76
const requestBody = await request.json();
87
console.log("Request Body:", requestBody);

components/DependencyGraph.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, { useEffect, useRef, useState } from 'react';
22
import * as d3 from 'd3';
3-
// import { useParams } from 'next/navigation';
43

54
export interface GraphDependency {
65
crate_name: string;
@@ -26,7 +25,6 @@ export interface DependencyGraphProps {
2625
const DependencyGraph: React.FC<DependencyGraphProps> = ({ crateName, currentVersion }) => {
2726
const [graphDependencies, setGraphDependencies] = useState<GraphDependency | null>(null);
2827
const d3Container = useRef<HTMLDivElement | null>(null);
29-
// const params = useParams();
3028

3129
useEffect(() => {
3230
async function fetchDependencyTree(name: string, version: string, visited: Set<string>): Promise<GraphDependency> {
@@ -112,10 +110,11 @@ const DependencyGraph: React.FC<DependencyGraphProps> = ({ crateName, currentVer
112110

113111
const nodes = Array.from(nodesMap.values());
114112

113+
// Remove the forceCenter to allow free movement of nodes
115114
const simulation = d3.forceSimulation<DependencyNode>(nodes)
116115
.force('link', d3.forceLink<DependencyNode, DependencyLink>(links).id(d => d.id).distance(100))
117116
.force('charge', d3.forceManyBody().strength(-500))
118-
.force('center', d3.forceCenter(width / 2, height / 2))
117+
// Remove center force to prevent auto centering
119118
.force('collide', d3.forceCollide().radius(50));
120119

121120
const link = svg.append('g')
@@ -191,11 +190,31 @@ const DependencyGraph: React.FC<DependencyGraphProps> = ({ crateName, currentVer
191190
d.fy = null;
192191
}
193192

193+
// Zoom functionality
194+
// const zoom = d3.zoom()
195+
// .scaleExtent([0.1, 10]) // Minimum and maximum zoom scales
196+
// .on('zoom', (event) => {
197+
// svg.selectAll('g')
198+
// .attr('transform', event.transform);
199+
// });
200+
201+
// svg.call(zoom as any);
202+
203+
// }, [graphDependencies]);
204+
const zoom = d3.zoom<SVGSVGElement, unknown>()
205+
.scaleExtent([0.1, 10]) // Minimum and maximum zoom scales
206+
.on('zoom', (event) => {
207+
svg.selectAll('g')
208+
.attr('transform', event.transform);
209+
});
210+
211+
svg.call(zoom); // 不再使用 any
212+
194213
}, [graphDependencies]);
195214

196215
return (
197-
<div className="bg-white p-4 mb-2 shadow-lg rounded-lg">
198-
<div ref={d3Container} style={{ width: '100%', height: '400px' }} />
216+
<div className="bg-white p-4 mb-2 shadow-lg rounded-lg h-screen w-full">
217+
<div ref={d3Container} style={{ width: '100%', height: '100%' }} />
199218
</div>
200219
);
201220
};

components/HeaderWithSearch.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const Header = () => {
2727
{ name: 'Overview', path: '' },
2828
{ name: 'Dependencies', path: '/dependencies' },
2929
{ name: 'Dependents', path: '/dependents' },
30+
{ name: 'Versions', path: '/versions' },
3031
];
3132

3233

components/VersionsTable.tsx

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
'use client';
2+
import React, { useEffect, useState, useCallback } from 'react';
3+
import { Table } from 'antd';
4+
import { useParams } from 'next/navigation';
5+
6+
// 假设后端接口返回的类型
7+
interface VersionInfo {
8+
version: string;
9+
publishDay: string;
10+
dependentsNumber: number;
11+
}
12+
13+
interface VersionsTableProps {
14+
data: string[] | undefined; // 传入的版本号数组
15+
}
16+
17+
const VersionsTable: React.FC<VersionsTableProps> = ({ data }) => {
18+
const [versionsData, setVersionsData] = useState<VersionInfo[]>([]);
19+
const [loading, setLoading] = useState<boolean>(false);
20+
const params = useParams();
21+
22+
// 获取版本发布日的函数
23+
const fetchPublishDay = useCallback(async (version: string) => {
24+
try {
25+
const response = await fetch(`/api/publish-day/${version}`);
26+
if (!response.ok) {
27+
throw new Error('Failed to fetch publish day');
28+
}
29+
const data = await response.json();
30+
return data.publishDay || 'N/A'; // 如果没有返回值,则默认返回 'N/A'
31+
} catch (error) {
32+
console.error('Error fetching publish day:', error);
33+
return 'N/A'; // 请求失败时返回默认值
34+
}
35+
}, []);
36+
37+
// 获取版本依赖数的函数
38+
const fetchDependentsNumber = useCallback(async (version: string) => {
39+
try {
40+
const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${version}/dependents`);
41+
if (!response.ok) {
42+
throw new Error('Failed to fetch dependents number');
43+
}
44+
const data = await response.json();
45+
return data.direct_count + data.indirect_count || 0; // 如果没有返回值,则默认返回 0
46+
} catch (error) {
47+
console.error('Error fetching dependents number:', error);
48+
return 0; // 请求失败时返回默认值
49+
}
50+
}, [params.nsfront, params.nsbehind, params.name]);
51+
52+
// 请求版本的发布日和依赖数
53+
const fetchVersionDetails = useCallback(async (version: string) => {
54+
const publishDay = await fetchPublishDay(version);
55+
const dependentsNumber = await fetchDependentsNumber(version);
56+
return { version, publishDay, dependentsNumber };
57+
}, [fetchPublishDay, fetchDependentsNumber]);
58+
59+
useEffect(() => {
60+
const fetchData = async () => {
61+
if (data && data.length > 0) {
62+
setLoading(true);
63+
const versionDetails = await Promise.all(
64+
data.map(async (version) => {
65+
return await fetchVersionDetails(version);
66+
})
67+
);
68+
setVersionsData(versionDetails);
69+
setLoading(false);
70+
}
71+
};
72+
73+
fetchData();
74+
}, [data, fetchVersionDetails]);
75+
76+
const columns = [
77+
{
78+
title: 'Version',
79+
dataIndex: 'version',
80+
key: 'version',
81+
render: (text: string) => <span>{text}</span>,
82+
},
83+
{
84+
title: 'Publish Day',
85+
dataIndex: 'publishDay',
86+
key: 'publishDay',
87+
render: (text: string) => <span>{text}</span>,
88+
},
89+
{
90+
title: 'Dependents Number',
91+
dataIndex: 'dependentsNumber',
92+
key: 'dependentsNumber',
93+
render: (text: number) => <span>{text}</span>,
94+
},
95+
];
96+
97+
return (
98+
<Table
99+
columns={columns}
100+
dataSource={versionsData}
101+
pagination={false}
102+
rowKey="version" // 使用版本字符串作为唯一键
103+
loading={loading} // 显示加载状态
104+
/>
105+
);
106+
};
107+
108+
export default VersionsTable;

0 commit comments

Comments
 (0)