Skip to content

Commit 139f8b2

Browse files
committed
feat: repo query
1 parent 3416cd1 commit 139f8b2

File tree

8 files changed

+199
-48
lines changed

8 files changed

+199
-48
lines changed

web/bun.lock

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

web/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
"@copilotkit/react-core": "^1.10.6",
1414
"@copilotkit/react-ui": "^1.10.6",
1515
"@copilotkit/runtime": "^1.10.6",
16+
"@octokit/rest": "^22.0.0",
1617
"@radix-ui/react-dropdown-menu": "^2.1.16",
1718
"@radix-ui/react-select": "^2.2.6",
1819
"@radix-ui/react-slot": "^1.2.3",
1920
"@radix-ui/react-use-controllable-state": "^1.2.2",
2021
"@shikijs/transformers": "^3.13.0",
22+
"@tanstack/react-query": "^5.90.5",
2123
"@xyflow/react": "^12.8.6",
2224
"better-auth": "^1.3.27",
2325
"class-variance-authority": "^0.7.1",

web/src/app/dashboard/page.tsx

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,47 @@ import { Search } from 'lucide-react';
66
import { DashboardSkeleton } from '@/features/dashboard/skeleton';
77
import { RepositoryCard } from '@/features/dashboard/repo-card';
88
import { ConnectGitHubView } from '@/features/dashboard/connect-github';
9-
10-
const MOCK_REPOS = [
11-
{ id: 1, title: 'Code Lens Pro', issueNumber: '#55', branch: 'main', avatar: 'https://avatars.githubusercontent.com/u/9919?s=40&v=4' },
12-
{ id: 2, title: 'Design System V2', issueNumber: '#102', branch: 'main', avatar: 'https://avatars.githubusercontent.com/u/1024025?s=40&v=4' },
13-
{ id: 3, title: 'API Service Refactor', issueNumber: '#89', branch: 'feat/new-auth', avatar: 'https://avatars.githubusercontent.com/u/9919?s=40&v=4' },
14-
{ id: 4, title: 'Documentation Site', issueNumber: '#21', branch: 'docs-update', avatar: 'https://avatars.githubusercontent.com/u/1024025?s=40&v=4' },
15-
{ id: 5, title: 'Mobile App POC', issueNumber: '#12', branch: 'main', avatar: 'https://avatars.githubusercontent.com/u/9919?s=40&v=4' },
16-
{ id: 6, title: 'E2E Testing Suite', issueNumber: '#76', branch: 'ci/playwright', avatar: 'https://avatars.githubusercontent.com/u/1024025?s=40&v=4' },
17-
];
9+
import { useRepoQuery } from '@/hooks/queries/useRepoQuery';
10+
import { authClient } from '@/lib/auth-client';
1811

1912
export default function GitHubIntegrationPage() {
2013
const [accounts, setAccounts] = useState<any[]>([]);
2114
const [searchQuery, setSearchQuery] = useState('');
22-
const [isLoading, setIsLoading] = useState(true);
15+
16+
const { data: repos, isPending, error } = useRepoQuery();
2317

2418
useEffect(() => {
2519
const fetchAccounts = async () => {
26-
await new Promise(resolve => setTimeout(resolve, 1000));
27-
setAccounts([{ providerId: 'github', accountId: 'user-123' }]);
28-
setIsLoading(false);
29-
};
20+
try {
21+
const { data: accountsList, error } = await authClient.listAccounts();
22+
if (accountsList) {
23+
console.log('Accounts:', accountsList);
24+
setAccounts(accountsList);
25+
}
26+
if (error) {
27+
console.error('Failed to fetch accounts:', error);
28+
}
29+
} catch (error) {
30+
console.error('Failed to fetch accounts:', error);
31+
}
32+
}
3033
fetchAccounts();
3134
}, []);
3235

3336
const isConnected = !!accounts.find(acc => acc.providerId === 'github');
3437

3538
const connectGitHub = async () => {
36-
setIsLoading(true);
37-
await new Promise(resolve => setTimeout(resolve, 1500));
38-
setAccounts([{ providerId: 'github', accountId: 'user-123' }]);
39-
setIsLoading(false);
39+
await authClient.linkSocial({
40+
provider: "github",
41+
callbackURL: '/dashboard'
42+
})
4043
};
4144

42-
const filteredRepos = MOCK_REPOS.filter(repo =>
43-
repo.title.toLowerCase().includes(searchQuery.toLowerCase())
45+
const filteredRepos = repos?.filter((repo: any) =>
46+
repo.name.toLowerCase().includes(searchQuery.toLowerCase())
4447
);
4548

46-
if (isLoading) {
49+
if (isPending) {
4750
return <DashboardSkeleton />;
4851
}
4952

@@ -76,7 +79,7 @@ export default function GitHubIntegrationPage() {
7679

7780
{/* --- Repository Grid --- */}
7881
<div className="overflow-y-auto grid grid-cols-1 md:grid-cols-3 gap-6 pt-6">
79-
{filteredRepos.map(repo => (
82+
{filteredRepos?.map((repo: any) => (
8083
<RepositoryCard key={repo.id} repo={repo} />
8184
))}
8285
</div>
Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,81 @@
11
import { Button } from "@/components/ui/button";
2-
import { GitFork, Github, GitPullRequestArrow } from "lucide-react";
2+
import { GitFork, Star, Lock, Unlock, ExternalLink, BarChart3 } from "lucide-react";
33
import Image from "next/image";
4+
import Link from "next/link";
5+
6+
interface Repo {
7+
name: string;
8+
description: string | null;
9+
html_url: string;
10+
private: boolean;
11+
language: string | null;
12+
stargazers_count: number;
13+
forks_count: number;
14+
owner: {
15+
avatar_url: string;
16+
};
17+
}
18+
19+
export const RepositoryCard = ({ repo }: { repo: Repo }) => {
20+
const handleAnalyze = () => {
21+
console.log(`Analyzing repository: ${repo.name}`);
22+
};
423

5-
export const RepositoryCard = ({ repo }: any) => {
624
return (
7-
<div className="flex h-max flex-col rounded-xl bg-black p-4 border border-zinc-800/80 transition-all duration-300 hover:border-zinc-700">
8-
<div className="flex-1">
9-
<p className="text-sm text-neutral-400 mb-1">Repository</p>
10-
<h3 className="text-md font-semibold text-neutral-100">{repo.title}</h3>
25+
<div className="
26+
relative flex h-full flex-col rounded-xl p-4 transition-all duration-300
27+
bg-white/5 backdrop-blur-lg
28+
border border-white/10
29+
hover:border-white/20 hover:bg-white/10
30+
">
31+
<Link
32+
href={repo.html_url}
33+
target="_blank"
34+
rel="noopener noreferrer"
35+
className="absolute top-4 right-4 text-neutral-500 hover:text-neutral-200 transition-colors"
36+
aria-label="Open repository on GitHub"
37+
>
38+
<ExternalLink className="h-4 w-4" />
39+
</Link>
40+
41+
<div className="flex items-center gap-2 pr-8">
42+
<h3 className="text-md font-semibold text-neutral-100 truncate">{repo.name}</h3>
43+
{repo.private ? <Lock className="h-3 w-3 text-neutral-500" /> : <Unlock className="h-3 w-3 text-neutral-500" />}
1144
</div>
45+
46+
<p className="text-sm text-neutral-400 mt-1 flex-1">
47+
{repo.description || "No description provided."}
48+
</p>
49+
1250
<div className="flex items-center justify-between mt-4">
1351
<div className="flex items-center gap-4 text-neutral-400 text-sm">
52+
{repo.language && (
53+
<span className="flex items-center gap-1.5 text-xs">
54+
<div className="w-2.5 h-2.5 rounded-full bg-blue-500" />
55+
{repo.language}
56+
</span>
57+
)}
1458
<span className="flex items-center gap-1.5 text-xs">
15-
<Github className="h-4 w-4" />{repo.issueNumber}
59+
<Star className="h-4 w-4" />{repo.stargazers_count}
1660
</span>
1761
<span className="flex items-center gap-1.5 text-xs">
18-
<GitFork className="h-4 w-4" />{repo.branch}
62+
<GitFork className="h-4 w-4" />{repo.forks_count}
1963
</span>
2064
</div>
2165
<div className="flex items-center gap-2">
22-
<Button variant="outline" size="sm" className="bg-zinc-800 hover:bg-zinc-700 text-neutral-200 border-zinc-700 text-xs">
23-
<GitPullRequestArrow className="h-4 w-4 mr-2" />
24-
Open
66+
<Button onClick={handleAnalyze} size="sm" className="cursor-pointer font-semibold text-xs bg-neutral-100 dark:bg-[#1C212D] text-neutral-900 dark:text-blue-400 border border-neutral-200 dark:border-blue-500/20">
67+
<BarChart3 className="h-4 w-4 mr-2" />
68+
Analyze
2569
</Button>
26-
<Image src={repo.avatar} alt="User Avatar" width={28} height={28} className="rounded-full" />
70+
<Image
71+
src={repo.owner.avatar_url}
72+
alt="Owner Avatar"
73+
width={28}
74+
height={28}
75+
className="rounded-full"
76+
/>
2777
</div>
2878
</div>
2979
</div>
3080
);
31-
};
81+
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { authClient } from "@/lib/auth-client";
2+
import { useQuery } from "@tanstack/react-query";
3+
import { Octokit } from "@octokit/rest";
4+
5+
const fetchRepos = async () => {
6+
7+
const { data } = await authClient.getAccessToken({
8+
providerId: "github",
9+
});
10+
11+
const accessToken = data?.accessToken;
12+
13+
if (!accessToken) {
14+
throw new Error("No access token found");
15+
}
16+
17+
const octokit = new Octokit({ auth: accessToken });
18+
19+
const repos = await octokit.paginate(
20+
octokit.rest.repos.listForAuthenticatedUser,
21+
{
22+
per_page: 100,
23+
sort: "updated",
24+
direction: "desc",
25+
}
26+
);
27+
28+
return repos;
29+
};
30+
31+
export const useRepoQuery = () => {
32+
return useQuery({
33+
queryKey: ["repo"],
34+
queryFn: () => fetchRepos(),
35+
});
36+
};

web/src/lib/auth.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { betterAuth } from "better-auth";
22
import { drizzleAdapter } from "better-auth/adapters/drizzle";
33
import { db } from "@/db";
4-
import * as schema from "@/db/schema/auth";
4+
import * as schema from "@/db/schema/auth";
55

66
export const auth = betterAuth({
77
account: {
8-
accountLinking: {
9-
allowDifferentEmails: true,
10-
updateUserInfoOnLink: true
11-
}
8+
accountLinking: {
9+
allowDifferentEmails: true,
10+
updateUserInfoOnLink: true,
11+
},
1212
},
1313
database: drizzleAdapter(db, {
1414
provider: "pg",
@@ -24,6 +24,7 @@ export const auth = betterAuth({
2424
github: {
2525
clientId: process.env.GITHUB_CLIENT_ID!,
2626
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
27+
scope: ["repo"]
2728
},
2829
},
2930
});

web/src/providers/app-provider.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,24 @@ import { ClerkProvider } from "@clerk/nextjs";
22
import { CopilotKit } from "@copilotkit/react-core";
33
import "@copilotkit/react-ui/styles.css";
44
import { ThemeProvider } from "next-themes";
5+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
6+
import { QueryProvider } from "./query-provider";
57

68
export const AppProvider = ({ children }: { children: React.ReactNode }) => {
9+
710
return (
811
<ClerkProvider>
912
<CopilotKit runtimeUrl="/api/copilotkit" agent="starterAgent">
10-
<ThemeProvider
11-
attribute="class"
12-
defaultTheme="dark"
13-
enableSystem
14-
disableTransitionOnChange
15-
>
16-
{children}
17-
</ThemeProvider>
13+
<QueryProvider>
14+
<ThemeProvider
15+
attribute="class"
16+
defaultTheme="dark"
17+
enableSystem
18+
disableTransitionOnChange
19+
>
20+
{children}
21+
</ThemeProvider>
22+
</QueryProvider>
1823
</CopilotKit>
1924
</ClerkProvider>
2025
);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use client'
2+
3+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4+
5+
const queryClient = new QueryClient({
6+
defaultOptions: {
7+
queries: {
8+
refetchOnWindowFocus: false,
9+
refetchOnMount: false,
10+
refetchOnReconnect: false,
11+
staleTime: 1000 * 60 * 5,
12+
},
13+
},
14+
})
15+
16+
export const QueryProvider = ({ children }: { children: React.ReactNode }) => {
17+
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
18+
}

0 commit comments

Comments
 (0)