Skip to content

Commit 4de4b53

Browse files
authored
feat: add GitHub integration page and sidebar item (#117)
- Add GitHub integration page at app/projects/[id]/github/page.tsx for repository management - Add "GitHub Integration" item to project sidebar navigation - Update homepage "Fullstack Agent" link to point to the GitHub repository
1 parent 431f7fb commit 4de4b53

3 files changed

Lines changed: 192 additions & 1 deletion

File tree

app/projects/[id]/github/page.tsx

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
'use client';
2+
3+
import { useState } from 'react';
4+
import { ExternalLink, Github, Loader2, RefreshCw } from 'lucide-react';
5+
import { useParams, useRouter } from 'next/navigation';
6+
import { toast } from 'sonner';
7+
8+
import { ConfigLayout } from '@/components/config/config-layout';
9+
import SettingsDialog from '@/components/dialog/settings-dialog';
10+
import { Button } from '@/components/ui/button';
11+
import { useProject } from '@/hooks/use-project';
12+
import { commitChanges, initializeRepo } from '@/lib/services/repoService';
13+
14+
export default function GithubPage() {
15+
const params = useParams();
16+
const projectId = params.id as string;
17+
const router = useRouter();
18+
19+
const { data: project, isLoading: projectLoading } = useProject(projectId);
20+
21+
const [isInitializing, setIsInitializing] = useState(false);
22+
const [isCommitting, setIsCommitting] = useState(false);
23+
const [showSettings, setShowSettings] = useState(false);
24+
25+
// Create a new repository on GitHub
26+
const handleInitialize = async () => {
27+
if (project?.githubRepo || isInitializing) return;
28+
29+
setIsInitializing(true);
30+
try {
31+
const result = await initializeRepo(projectId);
32+
if (result.success) {
33+
toast.success(result.message);
34+
router.refresh();
35+
} else {
36+
if (result.code === 'GITHUB_NOT_BOUND') {
37+
toast.error('Please connect your GitHub account first');
38+
setShowSettings(true);
39+
} else {
40+
toast.error(result.message);
41+
}
42+
}
43+
} catch (_error) {
44+
toast.error('An unexpected error occurred');
45+
} finally {
46+
setIsInitializing(false);
47+
}
48+
};
49+
50+
// Commit changes to the repository and push to GitHub
51+
const handleCommit = async () => {
52+
if (isCommitting) return;
53+
54+
setIsCommitting(true);
55+
try {
56+
const result = await commitChanges(projectId);
57+
if (result.success) {
58+
toast.success(result.message);
59+
} else {
60+
if (result.code === 'GITHUB_NOT_BOUND') {
61+
toast.error('Please connect your GitHub account first');
62+
setShowSettings(true);
63+
} else {
64+
toast.error(result.message);
65+
}
66+
}
67+
} catch (_error) {
68+
toast.error('Failed to commit changes');
69+
} finally {
70+
setIsCommitting(false);
71+
}
72+
};
73+
74+
return (
75+
<ConfigLayout
76+
title="GitHub Integration"
77+
description="Connect your project to GitHub for version control and collaboration"
78+
loading={projectLoading}
79+
>
80+
<div className="max-w-3xl space-y-6">
81+
{/* Connection Status Section */}
82+
<div className="p-6 bg-[#252526]/50 border border-[#3e3e42] rounded-lg">
83+
{/* Visual Header */}
84+
<div className="flex items-start gap-5 mb-8">
85+
<div className="p-3 bg-secondary/50 rounded-xl border border-border">
86+
<Github className="w-8 h-8 text-foreground" />
87+
</div>
88+
<div className="space-y-1">
89+
<h3 className="text-lg font-medium text-foreground">
90+
{project?.githubRepo ? 'Connected to GitHub' : 'GitHub Repository'}
91+
</h3>
92+
<p className="text-sm text-muted-foreground leading-relaxed max-w-lg">
93+
{project?.githubRepo
94+
? 'Your project is currently active and synced with a remote GitHub repository. You can push your latest changes below.'
95+
: 'Initialize a new repository to start tracking changes. This will create a private repository in your GitHub account and push the initial code.'}
96+
</p>
97+
</div>
98+
</div>
99+
100+
{/* Actions */}
101+
<div className="pl-[76px]">
102+
{project?.githubRepo ? (
103+
<div className="space-y-6">
104+
<div className="flex flex-col gap-2 p-3 bg-secondary/30 rounded-md border border-border/50">
105+
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wider">Repository URL</span>
106+
<a
107+
href={project.githubRepo}
108+
target="_blank"
109+
rel="noopener noreferrer"
110+
className="flex items-center gap-2 hover:underline text-primary hover:text-primary-hover font-mono text-sm break-all"
111+
>
112+
{project.githubRepo}
113+
<ExternalLink className="w-3.5 h-3.5" />
114+
</a>
115+
</div>
116+
117+
<div className="flex items-center gap-3">
118+
<Button
119+
onClick={handleCommit}
120+
disabled={isCommitting}
121+
className="min-w-[140px]"
122+
>
123+
{isCommitting ? (
124+
<>
125+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
126+
Pushing...
127+
</>
128+
) : (
129+
<>
130+
<RefreshCw className="mr-2 h-4 w-4" />
131+
Push Changes
132+
</>
133+
)}
134+
</Button>
135+
<Button
136+
variant="outline"
137+
onClick={() => window.open(project.githubRepo!, '_blank')}
138+
>
139+
View on GitHub
140+
</Button>
141+
</div>
142+
</div>
143+
) : (
144+
<Button
145+
onClick={handleInitialize}
146+
disabled={isInitializing}
147+
size="lg"
148+
>
149+
{isInitializing ? (
150+
<>
151+
<Loader2 className="mr-2 h-5 w-5 animate-spin" />
152+
Creating Repository...
153+
</>
154+
) : (
155+
<>
156+
<Github className="mr-2 h-5 w-5" />
157+
Initialize & Push to GitHub
158+
</>
159+
)}
160+
</Button>
161+
)}
162+
</div>
163+
</div>
164+
165+
{/* Global Settings Link */}
166+
<div className="flex items-center justify-between p-4 bg-secondary/20 border border-border/50 rounded-lg">
167+
<div className="space-y-1">
168+
<h4 className="text-sm font-medium text-foreground">GitHub Account Settings</h4>
169+
<p className="text-xs text-muted-foreground">Manage your global GitHub connection and personal access tokens.</p>
170+
</div>
171+
<Button variant="ghost" size="sm" onClick={() => setShowSettings(true)}>
172+
Open Settings
173+
</Button>
174+
</div>
175+
176+
<SettingsDialog
177+
open={showSettings}
178+
onOpenChange={setShowSettings}
179+
defaultTab="github"
180+
/>
181+
</div>
182+
</ConfigLayout>
183+
);
184+
}

components/home-page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export function HomePage() {
139139
>
140140
{getButtonText()}
141141
</Button>
142-
<Link href="https://fulling.ai/" target="_blank" rel="noopener">
142+
<Link href="https://github.com/FullAgent/fulling" target="_blank" rel="noopener">
143143
<Button
144144
size="lg"
145145
variant="secondary"

components/sidebars/project-sidebar-new.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
ChevronRight,
88
CreditCard,
99
Database,
10+
Github,
1011
Key,
1112
Package,
1213
Shield,
@@ -57,6 +58,12 @@ export default function ProjectSidebar({ project }: ProjectSidebarProps) {
5758
icon: CreditCard,
5859
href: `/projects/${project.id}/payment`,
5960
},
61+
{
62+
id: 'github',
63+
label: 'GitHub Integration',
64+
icon: Github,
65+
href: `/projects/${project.id}/github`,
66+
},
6067
];
6168

6269
return (

0 commit comments

Comments
 (0)