Skip to content

Commit 5a7430f

Browse files
committed
feat: enhance authentication forms with loading states and auth status checks
1 parent c339285 commit 5a7430f

File tree

3 files changed

+145
-54
lines changed

3 files changed

+145
-54
lines changed

components/auth/login-form.tsx

Lines changed: 92 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,24 @@ import { useRouter } from "next/navigation";
2626
import { useToast } from "@/hooks/use-toast";
2727
import { Separator } from "../ui/separator";
2828
import { SiGitlab } from "react-icons/si";
29+
import useSWR from "swr";
30+
import { AuthStatus } from "@/lib/types";
31+
import { Skeleton } from "../ui/skeleton";
2932

3033
const formSchema = z.object({
3134
username: z.string().min(1, "Username is required"),
3235
password: z.string().min(1, "Password is required"),
3336
});
3437

38+
const fetcher = (url: string) => api.get(url).then(res => res.data.data);
39+
3540
export function LoginForm() {
3641
const { login } = useAuth();
3742
const router = useRouter();
3843
const { toast } = useToast();
3944
const gitlabLoginUrl = `/api/v1/auth/gitlab/login`;
45+
46+
const { data: authStatus, isLoading } = useSWR<AuthStatus>('/auth/status', fetcher);
4047

4148
const form = useForm<z.infer<typeof formSchema>>({
4249
resolver: zodResolver(formSchema),
@@ -64,78 +71,109 @@ export function LoginForm() {
6471
});
6572
}
6673
};
74+
75+
if (isLoading) {
76+
return (
77+
<Card>
78+
<CardHeader>
79+
<CardTitle>Login to CSOJ</CardTitle>
80+
<CardDescription>
81+
Checking available login methods...
82+
</CardDescription>
83+
</CardHeader>
84+
<CardContent className="space-y-4">
85+
<Skeleton className="h-10 w-full" />
86+
<Skeleton className="h-10 w-full" />
87+
<div className="relative my-4">
88+
<div className="absolute inset-0 flex items-center">
89+
<Separator />
90+
</div>
91+
</div>
92+
<Skeleton className="h-10 w-full" />
93+
</CardContent>
94+
</Card>
95+
);
96+
}
6797

6898
return (
6999
<Card>
70100
<CardHeader>
71101
<CardTitle>Login to CSOJ</CardTitle>
72102
<CardDescription>
73-
Enter your credentials to access your account.
103+
{authStatus?.local_auth_enabled
104+
? "Enter your credentials or use an alternative login method."
105+
: "Please use an available login method."}
74106
</CardDescription>
75107
</CardHeader>
76108
<CardContent>
77-
<Form {...form}>
78-
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
79-
<FormField
80-
control={form.control}
81-
name="username"
82-
render={({ field }) => (
83-
<FormItem>
84-
<FormLabel>Username</FormLabel>
85-
<FormControl>
86-
<Input placeholder="your_username" {...field} />
87-
</FormControl>
88-
<FormMessage />
89-
</FormItem>
90-
)}
91-
/>
92-
<FormField
93-
control={form.control}
94-
name="password"
95-
render={({ field }) => (
96-
<FormItem>
97-
<FormLabel>Password</FormLabel>
98-
<FormControl>
99-
<Input type="password" placeholder="********" {...field} />
100-
</FormControl>
101-
<FormMessage />
102-
</FormItem>
103-
)}
104-
/>
105-
<Button
106-
type="submit"
107-
className="w-full"
108-
disabled={form.formState.isSubmitting}
109-
>
110-
{form.formState.isSubmitting ? "Logging in..." : "Login"}
111-
</Button>
112-
</form>
113-
</Form>
109+
{authStatus?.local_auth_enabled && (
110+
<>
111+
<Form {...form}>
112+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
113+
<FormField
114+
control={form.control}
115+
name="username"
116+
render={({ field }) => (
117+
<FormItem>
118+
<FormLabel>Username</FormLabel>
119+
<FormControl>
120+
<Input placeholder="your_username" {...field} />
121+
</FormControl>
122+
<FormMessage />
123+
</FormItem>
124+
)}
125+
/>
126+
<FormField
127+
control={form.control}
128+
name="password"
129+
render={({ field }) => (
130+
<FormItem>
131+
<FormLabel>Password</FormLabel>
132+
<FormControl>
133+
<Input type="password" placeholder="********" {...field} />
134+
</FormControl>
135+
<FormMessage />
136+
</FormItem>
137+
)}
138+
/>
139+
<Button
140+
type="submit"
141+
className="w-full"
142+
disabled={form.formState.isSubmitting}
143+
>
144+
{form.formState.isSubmitting ? "Logging in..." : "Login"}
145+
</Button>
146+
</form>
147+
</Form>
114148

115-
<div className="relative my-4">
116-
<div className="absolute inset-0 flex items-center">
149+
<div className="relative my-4">
150+
<div className="absolute inset-0 flex items-center">
117151
<Separator />
118-
</div>
119-
<div className="relative flex justify-center text-xs uppercase">
152+
</div>
153+
<div className="relative flex justify-center text-xs uppercase">
120154
<span className="bg-card px-2 text-muted-foreground">
121-
Or continue with
155+
Or continue with
122156
</span>
157+
</div>
123158
</div>
124-
</div>
159+
</>
160+
)}
125161

126162
<Button variant="outline" className="w-full" asChild>
127-
<a href={gitlabLoginUrl}>
128-
<SiGitlab className="mr-2 h-4 w-4" />
129-
Login with GitLab
130-
</a>
163+
<a href={gitlabLoginUrl}>
164+
<SiGitlab className="mr-2 h-4 w-4" />
165+
Login with GitLab
166+
</a>
131167
</Button>
132168

133-
<div className="mt-4 text-center text-sm">
134-
Don&apos;t have an account?{" "}
135-
<Link href="/register" className="underline">
136-
Register
137-
</Link>
138-
</div>
169+
{authStatus?.local_auth_enabled && (
170+
<div className="mt-4 text-center text-sm">
171+
Don&apos;t have an account?{" "}
172+
<Link href="/register" className="underline">
173+
Register
174+
</Link>
175+
</div>
176+
)}
139177
</CardContent>
140178
</Card>
141179
);

components/auth/register-form.tsx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,25 @@ import { Button } from "@/components/ui/button";
2323
import api from "@/lib/api";
2424
import { useRouter } from "next/navigation";
2525
import { useToast } from "@/hooks/use-toast";
26+
import useSWR from "swr";
27+
import { AuthStatus } from "@/lib/types";
28+
import { Skeleton } from "../ui/skeleton";
29+
2630

2731
const formSchema = z.object({
2832
username: z.string().min(3, "Username must be at least 3 characters"),
2933
nickname: z.string().min(1, "Nickname is required"),
3034
password: z.string().min(6, "Password must be at least 6 characters"),
3135
});
3236

37+
const fetcher = (url: string) => api.get(url).then(res => res.data.data);
38+
39+
3340
export function RegisterForm() {
3441
const router = useRouter();
3542
const { toast } = useToast();
43+
44+
const { data: authStatus, isLoading } = useSWR<AuthStatus>('/auth/status', fetcher);
3645

3746
const form = useForm<z.infer<typeof formSchema>>({
3847
resolver: zodResolver(formSchema),
@@ -63,6 +72,46 @@ export function RegisterForm() {
6372
});
6473
}
6574
};
75+
76+
if (isLoading) {
77+
return (
78+
<Card>
79+
<CardHeader>
80+
<CardTitle>Create an Account</CardTitle>
81+
<CardDescription>
82+
Checking registration availability...
83+
</CardDescription>
84+
</CardHeader>
85+
<CardContent className="space-y-4">
86+
<Skeleton className="h-10 w-full" />
87+
<Skeleton className="h-10 w-full" />
88+
<Skeleton className="h-10 w-full" />
89+
<Skeleton className="h-10 w-full" />
90+
</CardContent>
91+
</Card>
92+
);
93+
}
94+
95+
if (!authStatus?.local_auth_enabled) {
96+
return (
97+
<Card>
98+
<CardHeader>
99+
<CardTitle>Registration Disabled</CardTitle>
100+
<CardDescription>
101+
Account registration with username and password is not available.
102+
</CardDescription>
103+
</CardHeader>
104+
<CardContent>
105+
<p className="text-sm text-muted-foreground">
106+
Please return to the login page and use an alternative method.
107+
</p>
108+
<Button asChild className="mt-4 w-full">
109+
<Link href="/login">Back to Login</Link>
110+
</Button>
111+
</CardContent>
112+
</Card>
113+
);
114+
}
66115

67116
return (
68117
<Card>

lib/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ export interface Attempts {
9696
remaining: number | null;
9797
}
9898

99+
export interface AuthStatus {
100+
local_auth_enabled: boolean;
101+
}
102+
99103
export interface LinkItem {
100104
name: string;
101105
url: string;

0 commit comments

Comments
 (0)