Skip to content

Commit e7e54c0

Browse files
feat: improvise github connector flow ux (#613)
* chore(release): v0.1.0-alpha.71 [skip ci] * chore(release): update version.txt to v0.1.0-alpha.71 * feat: improvise github connector flow ux * fix: add missing translation keys to locales --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent f44759a commit e7e54c0

File tree

19 files changed

+755
-120
lines changed

19 files changed

+755
-120
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# [0.1.0-alpha.71](https://github.com/raghavyuva/nixopus/compare/v0.1.0-alpha.70...v0.1.0-alpha.71) (2025-12-04)
2+
3+
4+
### Features
5+
6+
* standardize page titles and headers across UI ([#588](https://github.com/raghavyuva/nixopus/issues/588)) ([f44759a](https://github.com/raghavyuva/nixopus/commit/f44759a34a0546ce497b7d2ea4a715c5ba6dd048))
7+
8+
9+
110
# [0.1.0-alpha.70](https://github.com/raghavyuva/nixopus/compare/v0.1.0-alpha.68...v0.1.0-alpha.70) (2025-12-01)
211

312

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nixopus",
3-
"version": "0.1.0-alpha.70",
3+
"version": "0.1.0-alpha.71",
44
"description": "A modern container management platform",
55
"private": false,
66
"cli-version": "0.1.28",

version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v0.1.0-alpha.70
1+
v0.1.0-alpha.71

view/app/self-host/components/github-connector/github-app-installer.tsx

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React from 'react';
2-
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
32
import { Button } from '@/components/ui/button';
43
import { Github } from 'lucide-react';
54
import { useTranslation } from '@/hooks/use-translation';
@@ -8,9 +7,11 @@ interface GithubInstallProps {
87
appSlug: string;
98
organization?: string;
109
callbackUrl: string;
10+
onSuccess?: () => void;
11+
onError?: (error: Error) => void;
1112
}
1213

13-
const GithubInstaller = ({ appSlug, organization, callbackUrl }: GithubInstallProps) => {
14+
const GithubInstaller = ({ appSlug, organization, callbackUrl, onSuccess, onError }: GithubInstallProps) => {
1415
const { t } = useTranslation();
1516

1617
const handleConnectGithub = () => {
@@ -27,25 +28,23 @@ const GithubInstaller = ({ appSlug, organization, callbackUrl }: GithubInstallPr
2728
};
2829

2930
return (
30-
<Card className="w-[350px]">
31-
<CardHeader>
32-
<CardTitle className="flex items-center gap-2">
31+
<div className="w-full max-w-md space-y-6">
32+
<div className="text-center space-y-2">
33+
<div className="flex items-center justify-center gap-2">
3334
<Github size={24} />
34-
{t('selfHost.githubInstaller.title')}
35-
</CardTitle>
36-
</CardHeader>
37-
<CardContent>
38-
<p className="mb-4 text-sm text-muted-foreground">
35+
<h3 className="text-lg font-semibold">{t('selfHost.githubInstaller.title')}</h3>
36+
</div>
37+
<p className="text-sm text-muted-foreground">
3938
{t('selfHost.githubInstaller.description')}
4039
</p>
41-
<Button className="w-full" onClick={handleConnectGithub}>
42-
{t('selfHost.githubInstaller.connectButton')}
43-
</Button>
44-
</CardContent>
45-
<CardFooter className="text-xs text-muted-foreground/40">
46-
<p>{t('selfHost.githubInstaller.terms')}</p>
47-
</CardFooter>
48-
</Card>
40+
</div>
41+
<Button className="w-full" onClick={handleConnectGithub} size="lg">
42+
{t('selfHost.githubInstaller.connectButton')}
43+
</Button>
44+
<p className="text-xs text-center text-muted-foreground/60">
45+
{t('selfHost.githubInstaller.terms')}
46+
</p>
47+
</div>
4948
);
5049
};
5150

view/app/self-host/components/github-connector/github-app-setup.tsx

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,96 @@
11
'use client';
2-
import React, { useState } from 'react';
3-
import { Card, CardContent } from '@/components/ui/card';
4-
import { GitHubAppCredentials } from '@/redux/types/github';
5-
import GitHubAppManifestComponent from './github-manifest-flow';
6-
import GithubInstaller from './github-app-installer';
2+
import React, { useState, useRef } from 'react';
3+
import { useGitHubAppSetup } from '../../hooks/use-github-app-setup';
4+
import { StepperNavigation } from './stepper-navigation';
5+
import { StepperControls } from './stepper-controls';
6+
import { StepPanel } from './step-panel';
77

88
interface GitHubAppSetupProps {
99
organization?: string;
1010
GetGithubConnectors: () => void;
1111
}
1212

1313
const GitHubAppSetup: React.FC<GitHubAppSetupProps> = ({ organization, GetGithubConnectors }) => {
14-
const [setupStage, setSetupStage] = useState<'registration' | 'installation'>('registration');
15-
const [credentials, setCredentials] = useState<GitHubAppCredentials | null>(null);
16-
const [error, setError] = useState<string | null>(null);
17-
18-
const handleRegistrationSuccess = async (creds: GitHubAppCredentials) => {
19-
setCredentials(creds);
20-
setSetupStage('installation');
21-
// await GetGithubConnectors();
14+
const handleGetGithubConnectors = async () => {
15+
await GetGithubConnectors();
2216
};
2317

24-
const handleRegistrationError = (error: Error) => {
25-
setError(`Registration failed: ${error.message}`);
18+
const createAppRef = useRef<(() => void) | null>(null);
19+
20+
const {
21+
Stepper,
22+
utils,
23+
credentials,
24+
error,
25+
setStepperMethods,
26+
handleRegistrationSuccess,
27+
handleRegistrationError,
28+
handleInstallationSuccess,
29+
handleInstallationError,
30+
handleNext,
31+
handleBack,
32+
getCanGoNext
33+
} = useGitHubAppSetup(handleGetGithubConnectors);
34+
35+
const handleCreateClick = (createFn: () => void) => {
36+
createAppRef.current = createFn;
2637
};
2738

28-
const handleInstallationError = (error: Error) => {
29-
setError(`Installation failed: ${error.message}`);
39+
const handleCreateApp = () => {
40+
if (createAppRef.current) {
41+
createAppRef.current();
42+
}
3043
};
3144

3245
return (
33-
<div className="flex flex-col items-center space-y-4 p-4">
34-
{setupStage === 'registration' ? (
35-
<GitHubAppManifestComponent
36-
organization={organization}
37-
onSuccess={handleRegistrationSuccess}
38-
onError={handleRegistrationError}
39-
/>
40-
) : credentials ? (
41-
<GithubInstaller appSlug={credentials.slug} organization={organization} callbackUrl={''} />
42-
) : null}
46+
<div className="flex flex-col items-center w-full max-w-4xl mx-auto p-4 space-y-6">
47+
<Stepper.Provider
48+
variant="horizontal"
49+
labelOrientation="vertical"
50+
initialStep="create-app"
51+
className="w-full"
52+
>
53+
{({ methods }) => {
54+
setStepperMethods(methods);
55+
const { current } = methods;
56+
const currentStepId = current.id;
57+
const isFirstStep = utils.getIndex(currentStepId) === 0;
58+
const isLastStep = utils.getLast().id === currentStepId;
59+
const canGoNext = Boolean(getCanGoNext(currentStepId));
60+
61+
return (
62+
<>
63+
<StepperNavigation Stepper={Stepper} />
64+
<div className="flex justify-center w-full">
65+
<div className="w-full max-w-2xl">
66+
<StepPanel
67+
Stepper={Stepper}
68+
currentStepId={currentStepId}
69+
credentials={credentials}
70+
error={error}
71+
organization={organization}
72+
onRegistrationSuccess={handleRegistrationSuccess}
73+
onRegistrationError={handleRegistrationError}
74+
onInstallationSuccess={handleInstallationSuccess}
75+
onInstallationError={handleInstallationError}
76+
onCreateClick={handleCreateClick}
77+
/>
78+
<StepperControls
79+
Stepper={Stepper}
80+
isFirstStep={isFirstStep}
81+
isLastStep={isLastStep}
82+
canGoNext={canGoNext}
83+
onBack={handleBack}
84+
onNext={handleNext}
85+
onCreateApp={handleCreateApp}
86+
currentStepId={currentStepId}
87+
/>
88+
</div>
89+
</div>
90+
</>
91+
);
92+
}}
93+
</Stepper.Provider>
4394
</div>
4495
);
4596
};

view/app/self-host/components/github-connector/github-manifest-flow.tsx

Lines changed: 41 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import React, { useState, useEffect } from 'react';
2-
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
1+
import React, { useState, useEffect, useMemo } from 'react';
32
import { Button } from '@/components/ui/button';
4-
import { Input } from '@/components/ui/input';
53
import { Alert, AlertDescription } from '@/components/ui/alert';
6-
import { Github, RefreshCw, Loader2 } from 'lucide-react';
4+
import { Github, Loader2 } from 'lucide-react';
75
import {
86
GitHubAppCredentials,
97
GitHubAppManifest,
@@ -51,10 +49,11 @@ const GitHubAppManifestComponent: React.FC<GitHubAppProps> = ({
5149
appUrl = process.env.NEXT_PUBLIC_APP_URL,
5250
redirectUrl = process.env.NEXT_PUBLIC_REDIRECT_URL,
5351
onSuccess,
54-
onError
52+
onError,
53+
onCreateClick
5554
}) => {
5655
const { t } = useTranslation();
57-
const [appName, setAppName] = useState<string>(generateRandomName());
56+
const appName = useMemo(() => generateRandomName(), []);
5857
const [status, setStatus] = useState<GitHubAppStatus>('initial');
5958
const [error, setError] = useState<string | null>(null);
6059
const [createGithubConnector, { isLoading, error: registerGithubAppError }] =
@@ -78,6 +77,12 @@ const GitHubAppManifestComponent: React.FC<GitHubAppProps> = ({
7877
}
7978
}, []);
8079

80+
useEffect(() => {
81+
if (onCreateClick && status === 'initial') {
82+
onCreateClick(createManifestForm);
83+
}
84+
}, [onCreateClick, status]);
85+
8186
const generateState = (): string => {
8287
return crypto
8388
.getRandomValues(new Uint8Array(16))
@@ -162,70 +167,37 @@ const GitHubAppManifestComponent: React.FC<GitHubAppProps> = ({
162167
}
163168
};
164169

170+
if (status === 'redirecting' || status === 'converting') {
171+
return (
172+
<div className="flex flex-col items-center gap-4 py-8">
173+
<Loader2 className="h-8 w-8 animate-spin" />
174+
<p>
175+
{status === 'redirecting'
176+
? t('selfHost.githubManifest.status.redirecting')
177+
: t('selfHost.githubManifest.status.converting')}
178+
</p>
179+
</div>
180+
);
181+
}
182+
183+
if (status === 'success') {
184+
return (
185+
<Alert>
186+
<AlertDescription className="text-green-600">
187+
{t('selfHost.githubManifest.status.success')}
188+
</AlertDescription>
189+
</Alert>
190+
);
191+
}
192+
165193
return (
166-
<Card className="w-[400px]">
167-
<CardHeader>
168-
<CardTitle className="flex items-center gap-2">
169-
<Github size={24} />
170-
{t('selfHost.githubManifest.title')}
171-
</CardTitle>
172-
</CardHeader>
173-
<CardContent className="space-y-4">
174-
{status === 'initial' && (
175-
<>
176-
<div className="space-y-2">
177-
<label className="text-sm font-medium">
178-
{t('selfHost.githubManifest.appName.label')}
179-
</label>
180-
<div className="flex gap-2">
181-
<Input
182-
value={appName}
183-
onChange={(e) => setAppName(e.target.value)}
184-
placeholder={t('selfHost.githubManifest.appName.placeholder')}
185-
className="flex-1"
186-
/>
187-
<Button
188-
variant="outline"
189-
size="icon"
190-
onClick={() => setAppName(generateRandomName())}
191-
title={t('selfHost.githubManifest.appName.generate')}
192-
>
193-
<RefreshCw size={16} />
194-
</Button>
195-
</div>
196-
</div>
197-
{error && (
198-
<Alert variant="destructive">
199-
<AlertDescription>{error}</AlertDescription>
200-
</Alert>
201-
)}
202-
203-
<Button className="w-full" onClick={createManifestForm}>
204-
{t('selfHost.githubManifest.createButton')}
205-
</Button>
206-
</>
207-
)}
208-
209-
{(status === 'redirecting' || status === 'converting') && (
210-
<div className="flex flex-col items-center gap-4 py-8">
211-
<Loader2 className="h-8 w-8 animate-spin" />
212-
<p>
213-
{status === 'redirecting'
214-
? t('selfHost.githubManifest.status.redirecting')
215-
: t('selfHost.githubManifest.status.converting')}
216-
</p>
217-
</div>
218-
)}
219-
220-
{status === 'success' && (
221-
<Alert>
222-
<AlertDescription className="text-green-600">
223-
{t('selfHost.githubManifest.status.success')}
224-
</AlertDescription>
225-
</Alert>
226-
)}
227-
</CardContent>
228-
</Card>
194+
<div className="flex flex-col items-center gap-4 w-full">
195+
{error && (
196+
<Alert variant="destructive" className="w-full">
197+
<AlertDescription>{error}</AlertDescription>
198+
</Alert>
199+
)}
200+
</div>
229201
);
230202
};
231203

0 commit comments

Comments
 (0)