Skip to content

Commit 16c3f00

Browse files
feat: use new api endpoints (#34)
1 parent 880c1f6 commit 16c3f00

25 files changed

Lines changed: 1233 additions & 1342 deletions

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ A modern, responsive web application for discovering and exploring modules and d
44

55
<img width="1277" height="773" alt="Знімок екрана 2025-08-06 о 17 01 00" src="https://github.com/user-attachments/assets/8be09316-6d87-47ca-8b70-15619d3d5f1f" />
66

7-
87
## 🚀 Features
98

109
- **Dual Interface**: Browse both modules and data sources with dedicated views
@@ -69,6 +68,7 @@ A modern, responsive web application for discovering and exploring modules and d
6968
The application fetches data from multiple sources:
7069

7170
### Modules Data
71+
7272
- **ASIMOV Platform API**: Module metadata, stars, and GitHub information
7373
- **GitHub API**: Live repository data and statistics
7474
- **YAML Manifests**: Module configuration and metadata
@@ -91,6 +91,7 @@ The project uses a custom color palette defined in `src/styles/global.css`:
9191
### Navigation Structure
9292

9393
Two main sections:
94+
9495
- **Sources** (`/`): Browse data sources with endpoints and formats
9596
- **Modules** (`/modules`): Explore GitHub modules with metadata
9697

@@ -165,6 +166,7 @@ npm run format:check # Check code formatting
165166
## 📊 Performance & Caching
166167

167168
### Query Caching
169+
168170
- **TanStack Query**: Intelligent data caching with stale-while-revalidate
169171
- **Optimistic Updates**: Smooth user experience with background refetching
170172
- **Error Recovery**: Automatic retry logic for failed requests

package-lock.json

Lines changed: 401 additions & 375 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"devDependencies": {
1717
"@astrojs/sitemap": "^3.2.1",
1818
"@eslint/compat": "^1.2.3",
19-
"@types/js-yaml": "^4.0.9",
2019
"@typescript-eslint/eslint-plugin": "^8.0.0",
2120
"@typescript-eslint/parser": "^8.0.0",
2221
"eslint": "^9.32.0",
@@ -41,6 +40,8 @@
4140
"astro": "^5.12.8",
4241
"react": "^19.1.1",
4342
"react-dom": "^19.1.1",
44-
"tailwindcss": "^4.1.11"
43+
"react-infinite-scroll-component": "^6.1.0",
44+
"tailwindcss": "^4.1.11",
45+
"yaml": "^2.8.1"
4546
}
4647
}

src/components/GitHubStats.tsx

Lines changed: 43 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,60 @@
11
import { useQuery } from '@tanstack/react-query';
22
import { Star, Users } from '@phosphor-icons/react';
3-
import { githubApi, formatStars } from '../lib/github';
3+
import { fetchTotalModuleStars, fetchOrgFollowers } from '../lib/github';
44
import { asimovModulesOrgUrl, asimovPlatformOrgUrl } from '../lib/config';
5+
import { formatStars } from '../lib/utils';
56
import { queryClient } from '../store';
67

8+
import MetricsBadge from './MetricsBadge';
9+
710
interface GitHubStatsProps {
811
variant?: 'desktop' | 'mobile';
912
}
1013

1114
export default function GitHubStats({ variant = 'desktop' }: GitHubStatsProps) {
12-
const githubStatsQuery = useQuery({
13-
queryKey: ['github-stats'],
14-
queryFn: () => githubApi.fetchGitHubStats(),
15-
staleTime: 10 * 60 * 1000, // 10 minutes
16-
gcTime: 20 * 60 * 1000, // 20 minutes
17-
retry: 1
18-
}, queryClient);
15+
const { data: stars, isLoading: isStarsLoading } = useQuery(
16+
{
17+
queryKey: ['asimov-modules-stars'],
18+
queryFn: fetchTotalModuleStars,
19+
staleTime: 10 * 60 * 1000, // 10 minutes
20+
gcTime: 20 * 60 * 1000, // 20 minutes
21+
retry: 2
22+
},
23+
queryClient
24+
);
25+
26+
const { data: followers, isLoading: isFollowersLoading } = useQuery(
27+
{
28+
queryKey: ['asimov-platform-followers'],
29+
queryFn: fetchOrgFollowers,
30+
staleTime: 10 * 60 * 1000, // 10 minutes
31+
gcTime: 20 * 60 * 1000, // 20 minutes
32+
retry: 2
33+
},
34+
queryClient
35+
);
1936

2037
const containerClass =
2138
variant === 'desktop'
2239
? 'flex items-center gap-3'
2340
: 'flex items-center justify-center gap-4 py-2';
2441

25-
const linkClass =
26-
variant === 'desktop'
27-
? 'group flex items-center gap-1.5 rounded-lg border border-gray-200 bg-white/80 px-3 py-1.5 text-sm transition-all hover:border-orange-200 hover:bg-orange-50'
28-
: 'flex items-center gap-1.5 rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-sm';
29-
30-
const iconClass =
31-
variant === 'desktop' ? 'text-gray-500 group-hover:text-orange-600' : 'text-gray-500';
32-
33-
const textClass =
34-
variant === 'desktop'
35-
? 'font-medium text-gray-700 group-hover:text-orange-700'
36-
: 'font-medium text-gray-700';
37-
38-
const loadingContainerClass =
39-
variant === 'desktop'
40-
? 'flex items-center gap-3'
41-
: 'flex items-center justify-center gap-4 py-2';
42-
43-
if (githubStatsQuery.data) {
44-
return (
45-
<div className={containerClass}>
46-
<a
47-
href={asimovModulesOrgUrl}
48-
target="_blank"
49-
rel="noopener noreferrer"
50-
className={linkClass}
51-
>
52-
<Star size={14} className={iconClass} />
53-
<span className={textClass}>
54-
{formatStars(githubStatsQuery.data.stars)}
55-
</span>
56-
</a>
57-
<a
58-
href={asimovPlatformOrgUrl}
59-
target="_blank"
60-
rel="noopener noreferrer"
61-
className={linkClass}
62-
>
63-
<Users size={14} className={iconClass} />
64-
<span className={textClass}>
65-
{formatStars(githubStatsQuery.data.followers)}
66-
</span>
67-
</a>
68-
</div>
69-
);
70-
}
71-
72-
if (githubStatsQuery.isLoading) {
73-
return (
74-
<div className={loadingContainerClass}>
75-
<div className="animate-pulse rounded-lg bg-gray-200 px-3 py-1.5">
76-
<div className="h-4 w-8 rounded bg-gray-300"></div>
77-
</div>
78-
<div className="animate-pulse rounded-lg bg-gray-200 px-3 py-1.5">
79-
<div className="h-4 w-8 rounded bg-gray-300"></div>
80-
</div>
81-
</div>
82-
);
83-
}
84-
return null;
42+
return (
43+
<div className={containerClass}>
44+
<MetricsBadge
45+
value={formatStars(stars ?? 0)}
46+
href={asimovModulesOrgUrl}
47+
icon={Star}
48+
variant={variant}
49+
loading={isStarsLoading}
50+
/>
51+
<MetricsBadge
52+
value={formatStars(followers ?? 0)}
53+
href={asimovPlatformOrgUrl}
54+
icon={Users}
55+
variant={variant}
56+
loading={isFollowersLoading}
57+
/>
58+
</div>
59+
);
8560
}

src/components/MetricsBadge.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
interface MetricsBadgeProps {
2+
href: string;
3+
value: string | number;
4+
icon: React.ComponentType<{ size: number; className?: string }>;
5+
variant?: 'desktop' | 'mobile';
6+
loading?: boolean;
7+
}
8+
9+
export default function MetricsBadge({
10+
href,
11+
value,
12+
icon: Icon,
13+
variant,
14+
loading
15+
}: MetricsBadgeProps) {
16+
const linkClass =
17+
variant === 'desktop'
18+
? 'group flex items-center gap-1.5 rounded-lg border border-gray-200 bg-white/80 px-3 py-1.5 text-sm transition-all hover:border-orange-200 hover:bg-orange-50'
19+
: 'flex items-center gap-1.5 rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-sm';
20+
21+
const iconClass =
22+
variant === 'desktop' ? 'text-gray-500 group-hover:text-orange-600' : 'text-gray-500';
23+
24+
const textClass =
25+
variant === 'desktop'
26+
? 'font-medium text-gray-700 group-hover:text-orange-700'
27+
: 'font-medium text-gray-700';
28+
29+
return (
30+
<>
31+
{loading ? (
32+
<div className="animate-pulse rounded-lg bg-gray-200 px-3 py-1.5">
33+
<div className="h-4 w-8 rounded bg-gray-300"></div>
34+
</div>
35+
) : (
36+
<a href={href} target="_blank" rel="noopener noreferrer" className={linkClass}>
37+
<Icon size={14} className={iconClass} />
38+
<span className={textClass}>{value}</span>
39+
</a>
40+
)}
41+
</>
42+
);
43+
}

0 commit comments

Comments
 (0)