2. 認証のUIを実装
ログイン画面の作成
あまり深堀りする必要がないのでコードのみ
src/features/auth/components/auth-screen.tsx
"use client";
import { useState } from "react";
import { SignInFlow } from "../types";
import { SignInCard } from "./sign-in-catd";
import { SignUpCard } from "./sign-up-catd";
export const AuthScreen = () => {
const [state, setState] = useState<SignInFlow>("signIn");
return (
<div className="h-full flex items-center justify-center bg-[#5C3B58]">
<div className="md:h-auto md:w-[420px]">
{state === "signIn" ? (
<SignInCard setState={setState} />
) : (
<SignUpCard setState={setState} />
)}
</div>
</div>
);
};
src/features/auth/components/sign-in-card.tsx
import { FaGithub } from "react-icons/fa";
import { FcGoogle } from "react-icons/fc";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { useState } from "react";
import { SignInFlow } from "../types";
interface SignInCardProps {
setState: (state: SignInFlow) => void;
}
export const SignInCard = ({ setState }: SignInCardProps) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
return (
<Card className="w-full h-full p-8">
<CardHeader className="px-0 pt-0">
<CardTitle>Login to continue</CardTitle>
<CardDescription className="">
User your email or another sercuce to continue
</CardDescription>
</CardHeader>
<CardContent className="space-y-5 px-0 pb-0">
<form className="space-y-2.5">
<Input
disabled={false}
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
type="email"
required
/>
<Input
disabled={false}
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
type="password"
required
/>
<Button type="submit" className="w-full" size="lg" disabled={false}>
Continue
</Button>
</form>
<Separator />
<div className="flex flex-col gap-y-2.5">
<Button
disabled={false}
onClick={() => {}}
variant={"outline"}
size="lg"
className="w-full relative"
>
<FcGoogle className="size-5 absolute top-3 left-2.5" />
Continue With Google
</Button>
<Button
disabled={false}
onClick={() => {}}
variant={"outline"}
size="lg"
className="w-full relative"
>
<FaGithub className="size-5 absolute top-2.5 left-2.5" />
Continue With Github
</Button>
</div>
<p className="text-xs text-muted-foreground">
Don't have an account?{" "}
<span
onClick={() => setState("signUp")}
className="text-sky-700 hover:underline cursor-pointer"
>
Sign up
</span>
</p>
</CardContent>
</Card>
);
};
src/features/auth/components/sign-up-card.tsx
import { FaGithub } from "react-icons/fa";
import { FcGoogle } from "react-icons/fc";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { useState } from "react";
import { SignInFlow } from "../types";
interface SignUpCardProps {
setState: (state: SignInFlow) => void;
}
export const SignUpCard = ({ setState }: SignUpCardProps) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
return (
<Card className="w-full h-full p-8">
<CardHeader className="px-0 pt-0">
<CardTitle>Sign up to continue</CardTitle>
<CardDescription className="">
User your email or another sercuce to continue
</CardDescription>
</CardHeader>
<CardContent className="space-y-5 px-0 pb-0">
<form className="space-y-2.5">
<Input
disabled={false}
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
type="email"
required
/>
<Input
disabled={false}
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
type="password"
required
/>
<Input
disabled={false}
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
placeholder="Confirm Password"
type="password"
required
/>
<Button type="submit" className="w-full" size="lg" disabled={false}>
Continue
</Button>
</form>
<Separator />
<div className="flex flex-col gap-y-2.5">
<Button
disabled={false}
onClick={() => {}}
variant={"outline"}
size="lg"
className="w-full relative"
>
<FcGoogle className="size-5 absolute top-3 left-2.5" />
Continue With Google
</Button>
<Button
disabled={false}
onClick={() => {}}
variant={"outline"}
size="lg"
className="w-full relative"
>
<FaGithub className="size-5 absolute top-2.5 left-2.5" />
Continue With Github
</Button>
</div>
<p className="text-xs text-muted-foreground">
Already have an account?{" "}
<span
onClick={() => setState("signIn")}
className="text-sky-700 hover:underline cursor-pointer"
>
Sign in
</span>
</p>
</CardContent>
</Card>
);
};
Tips
コンポーネントディレクトリでtailwindが効かない
これはtailwindのコンフィグのディレクトリ指定にない場所においている場合に発生する。
tailwind.config.ts
import type { Config } from "tailwindcss";
const config: Config = {
darkMode: ["class"],
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
"./src/features/**/*.{js,ts,jsx,tsx,mdx}",
],