Skip to content

Commit 587bbd9

Browse files
committed
feat(desktop): GitHub-only login, loading state, fix repo picker UX
- Remove email/password login — single GitHub sign-in button - Show spinner with 'Waiting for GitHub...' after click - Repo picker shows retry instead of asking to connect again - Remove unused getLoginUrl fallback from projects page
1 parent b234366 commit 587bbd9

File tree

2 files changed

+45
-187
lines changed

2 files changed

+45
-187
lines changed

apps/desktop-ui/app/login/page.tsx

Lines changed: 29 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,21 @@
22

33
import { useEffect, useState } from "react"
44
import { useRouter } from "next/navigation"
5-
import { Github, Loader2, UserPlus } from "lucide-react"
5+
import { Github, Loader2 } from "lucide-react"
66
import { Button } from "@/components/ui/button"
7-
import { Input } from "@/components/ui/input"
8-
import { Label } from "@/components/ui/label"
9-
import { loginUser, registerUser, getLoginUrl } from "@/lib/api"
107
import { useAuth } from "@/hooks/use-auth"
118
import { openExternal } from "@/lib/utils"
12-
13-
type Tab = "email" | "github"
9+
import { getLoginUrl } from "@/lib/api"
1410

1511
export default function LoginPage() {
1612
const router = useRouter()
17-
const { refreshUser, isAuthenticated, isLoading: authLoading } = useAuth()
18-
const [activeTab, setActiveTab] = useState<Tab>("email")
19-
const [username, setUsername] = useState("")
20-
const [password, setPassword] = useState("")
13+
const { isAuthenticated, isLoading: authLoading } = useAuth()
2114
const [error, setError] = useState<string | null>(null)
2215
const [isLoading, setIsLoading] = useState(false)
23-
const [isRegistering, setIsRegistering] = useState(false)
2416

25-
const handleLogin = async (e: React.FormEvent) => {
26-
e.preventDefault()
27-
setError(null)
17+
const handleGitHubLogin = async () => {
2818
setIsLoading(true)
29-
30-
try {
31-
const data = await loginUser(username, password)
32-
localStorage.setItem("token", data.token)
33-
await refreshUser()
34-
router.push("/")
35-
} catch (err) {
36-
setError(err instanceof Error ? err.message : "Login failed")
37-
} finally {
38-
setIsLoading(false)
39-
}
40-
}
41-
42-
const handleRegister = async (e: React.FormEvent) => {
43-
e.preventDefault()
4419
setError(null)
45-
setIsLoading(true)
46-
47-
try {
48-
const data = await registerUser(username, password)
49-
localStorage.setItem("token", data.token)
50-
await refreshUser()
51-
router.push("/")
52-
} catch (err) {
53-
setError(err instanceof Error ? err.message : "Registration failed")
54-
} finally {
55-
setIsLoading(false)
56-
}
57-
}
58-
59-
const handleGitHubLogin = async () => {
6020
try {
6121
await openExternal(getLoginUrl())
6222
} catch {
@@ -77,7 +37,7 @@ export default function LoginPage() {
7737
<div className="text-center mb-8">
7838
<img
7939
src="/logo.png"
80-
alt="OpenLinear"
40+
alt="OpenLinear"
8141
className="h-12 mx-auto mb-4"
8242
/>
8343
<p className="text-sm text-linear-text-secondary">
@@ -86,127 +46,35 @@ export default function LoginPage() {
8646
</div>
8747

8848
{/* Card */}
89-
<div className="bg-linear-bg-secondary border border-linear-border rounded-lg overflow-hidden">
90-
{/* Tab Buttons */}
91-
<div className="flex border-b border-linear-border">
92-
<button
93-
type="button"
94-
onClick={() => setActiveTab("email")}
95-
className={`flex-1 px-4 py-3 text-sm font-medium transition-colors ${
96-
activeTab === "email"
97-
? "text-linear-text border-b-2 border-linear-accent"
98-
: "text-linear-text-secondary hover:text-linear-text"
99-
}`}
100-
>
101-
Email / Password
102-
</button>
103-
<button
104-
type="button"
105-
onClick={() => setActiveTab("github")}
106-
className={`flex-1 px-4 py-3 text-sm font-medium transition-colors ${
107-
activeTab === "github"
108-
? "text-linear-text border-b-2 border-linear-accent"
109-
: "text-linear-text-secondary hover:text-linear-text"
110-
}`}
111-
>
112-
GitHub
113-
</button>
114-
</div>
115-
116-
{/* Content */}
117-
<div className="p-6">
118-
{activeTab === "email" && (
119-
<div className="space-y-4">
120-
<form onSubmit={isRegistering ? handleRegister : handleLogin} className="space-y-4">
121-
<div className="space-y-2">
122-
<Label htmlFor="username" className="text-linear-text">
123-
Username
124-
</Label>
125-
<Input
126-
id="username"
127-
type="text"
128-
placeholder="Enter your username"
129-
value={username}
130-
onChange={(e) => setUsername(e.target.value)}
131-
className="bg-linear-bg border-linear-border text-linear-text placeholder:text-linear-text-tertiary focus:border-linear-accent"
132-
required
133-
/>
134-
</div>
135-
136-
<div className="space-y-2">
137-
<Label htmlFor="password" className="text-linear-text">
138-
Password
139-
</Label>
140-
<Input
141-
id="password"
142-
type="password"
143-
placeholder="Enter your password"
144-
value={password}
145-
onChange={(e) => setPassword(e.target.value)}
146-
className="bg-linear-bg border-linear-border text-linear-text placeholder:text-linear-text-tertiary focus:border-linear-accent"
147-
required
148-
/>
149-
</div>
150-
151-
{error && (
152-
<div className="p-3 rounded-md bg-destructive/10 border border-destructive/20">
153-
<p className="text-sm text-destructive">{error}</p>
154-
</div>
155-
)}
156-
157-
<div className="flex gap-3 pt-2">
158-
<Button
159-
type="submit"
160-
disabled={isLoading}
161-
className="flex-1 bg-linear-accent hover:bg-linear-accent-hover text-white"
162-
>
163-
{isLoading ? (
164-
<>
165-
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
166-
{isRegistering ? "Creating account..." : "Signing in..."}
167-
</>
168-
) : (
169-
isRegistering ? "Create Account" : "Sign In"
170-
)}
171-
</Button>
172-
<Button
173-
type="button"
174-
variant="outline"
175-
onClick={() => {
176-
setIsRegistering(!isRegistering)
177-
setError(null)
178-
}}
179-
className="border-linear-border text-linear-text-secondary hover:text-linear-text hover:bg-linear-bg-tertiary"
180-
>
181-
<UserPlus className="w-4 h-4 mr-2" />
182-
{isRegistering ? "Back to Login" : "Register"}
183-
</Button>
184-
</div>
185-
</form>
49+
<div className="bg-linear-bg-secondary border border-linear-border rounded-lg p-6">
50+
<div className="space-y-4">
51+
<p className="text-sm text-linear-text-secondary text-center">
52+
Sign in with your GitHub account to access OpenLinear
53+
</p>
54+
55+
{error && (
56+
<div className="p-3 rounded-md bg-destructive/10 border border-destructive/20">
57+
<p className="text-sm text-destructive">{error}</p>
18658
</div>
18759
)}
18860

189-
{activeTab === "github" && (
190-
<div className="space-y-4">
191-
<p className="text-sm text-linear-text-secondary text-center">
192-
Sign in with your GitHub account to access OpenLinear
193-
</p>
194-
195-
{error && (
196-
<div className="p-3 rounded-md bg-destructive/10 border border-destructive/20">
197-
<p className="text-sm text-destructive">{error}</p>
198-
</div>
199-
)}
200-
201-
<Button
202-
onClick={handleGitHubLogin}
203-
className="w-full bg-[#24292e] hover:bg-[#1b1f23] text-white"
204-
>
61+
<Button
62+
onClick={handleGitHubLogin}
63+
disabled={isLoading}
64+
className="w-full bg-[#24292e] hover:bg-[#1b1f23] text-white disabled:opacity-70"
65+
>
66+
{isLoading ? (
67+
<>
68+
<Loader2 className="w-5 h-5 mr-2 animate-spin" />
69+
Waiting for GitHub...
70+
</>
71+
) : (
72+
<>
20573
<Github className="w-5 h-5 mr-2" />
20674
Sign in with GitHub
207-
</Button>
208-
</div>
209-
)}
75+
</>
76+
)}
77+
</Button>
21078
</div>
21179
</div>
21280

apps/desktop-ui/app/projects/page.tsx

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ import {
4848
updateProject,
4949
deleteProject,
5050
fetchGitHubRepos,
51-
getLoginUrl,
5251
getGitHubConnectUrl,
5352
type Project,
5453
type Team,
@@ -209,13 +208,8 @@ function ProjectsContent() {
209208
}, [])
210209

211210
const openGitHubConnect = useCallback(async () => {
212-
try {
213-
const url = await getGitHubConnectUrl()
214-
await openExternal(url)
215-
} catch (error) {
216-
console.error("Failed to start GitHub connection:", error)
217-
await openExternal(getLoginUrl())
218-
}
211+
const url = await getGitHubConnectUrl()
212+
await openExternal(url)
219213
}, [])
220214

221215
useEffect(() => {
@@ -695,15 +689,13 @@ function ProjectsContent() {
695689
</div>
696690
) : githubError ? (
697691
<div className="text-sm text-linear-text-secondary py-4">
698-
Connect your GitHub account to browse repos.{" "}
699-
<button
700-
type="button"
701-
onClick={() => {
702-
void openGitHubConnect()
703-
}}
704-
className="text-linear-accent hover:underline"
705-
>
706-
Connect GitHub
692+
Failed to load repos.{" "}
693+
<button
694+
type="button"
695+
onClick={() => loadGitHubRepos()}
696+
className="text-linear-accent hover:underline"
697+
>
698+
Retry
707699
</button>
708700
</div>
709701
) : (
@@ -1260,15 +1252,13 @@ function ProjectsContent() {
12601252
</div>
12611253
) : editGithubError ? (
12621254
<div className="text-sm text-linear-text-secondary py-4">
1263-
Connect your GitHub account to browse repos.{" "}
1264-
<button
1265-
type="button"
1266-
onClick={() => {
1267-
void openGitHubConnect()
1268-
}}
1269-
className="text-linear-accent hover:underline"
1270-
>
1271-
Connect GitHub
1255+
Failed to load repos.{" "}
1256+
<button
1257+
type="button"
1258+
onClick={() => loadGitHubRepos(true)}
1259+
className="text-linear-accent hover:underline"
1260+
>
1261+
Retry
12721262
</button>
12731263
</div>
12741264
) : (

0 commit comments

Comments
 (0)