サインアップページの作成
定数の定義
src/frontend/src/constants/messages.ts
// ↓追加
/**
* メッセージの定数
*/
export const MESSAGES = {
SIGN_UP_SUCCESS: "ユーザ登録が完了しました。サインインしてください。",
} as const;
// ↑追加
/**
* エラーメッセージの定数
*/
export const ERROR_MESSAGES = {
// ↓追加
// 汎用エラーメッセージ生成関数
ERROR_IN_METHOD: (methodName: string) => `Error in ${methodName}:`,
// ↑追加
// API関連
API_REQUEST_ERROR: "APIリクエスト中にエラーが発生しました",
// ↓追加
// 認証関連
EMAIL_AND_PASSWORD_REQUIRED: "メールアドレスとパスワードを入力してください。",
SIGN_UP_FAILED:
"サインアップに失敗しました。時間をおいて再度お試しください。",
// ↑追加
} as const;
src/frontend/src/constants/styles.ts
/**
* スタイル関連の定数
*/
export const STYLES = {
...(略)...
// ↓追加
// 認証関連
SIGN_PAGE: `flex min-h-screen items-center justify-center bg-gray-100`,
SIGN_PAGE_CARD: `w-full max-w-md rounded-lg border border-gray-300 bg-white p-8 shadow-lg`,
SIGN_PAGE_TITLE: `mb-6 text-center text-2xl font-bold`,
SIGN_FORM: `flex flex-col gap-4`,
SIGN_FORM_LABEL_INPUT: `flex flex-col gap-1`,
SIGN_FORM_LABEL: `text-sm font-medium`,
SIGN_FORM_INPUT: `rounded border border-gray-500 px-3 py-2 text-sm outline-none focus:ring-4 focus:ring-indigo-400`,
SIGN_FORM_BUTTON: `cursor-pointer rounded bg-indigo-400 px-2 py-1 font-semibold text-white hover:bg-indigo-600 focus:ring-4 focus:ring-indigo-400 disabled:cursor-not-allowed disabled:bg-gray-300`,
SIGN_FORM_ERROR: `text-rose-600`,
SIGN_FORM_HINT: `mt-2 text-xs text-gray-500`,
// ↑追加
} as const;
ユーティリティ関数の定義
src/frontend/src/utils/functions.ts
/**
* 関数名を自動取得するヘルパー関数
* @returns 現在の関数名
*/
export const getFunctionName = (): string => {
const stack = new Error().stack;
if (stack) {
const lines = stack.split("\n");
// lines[1] = getFunctionName 自身、lines[2] = 呼び出し元の関数
if (lines.length > 2) {
const match = lines[2].match(/at\s+(\w+)/);
return match ? match[1] : "unknown";
}
}
return "unknown";
};
src/frontend/src/utils/index.ts
export * from "./api";
export * from "./functions"; // <- 追加
サインアップページ
src/frontend/src/app/signup/page.tsx
"use client";
import React, { useState } from "react";
import { MESSAGES, ERROR_MESSAGES, STYLES } from "@/constants";
import { getFunctionName } from "@/utils";
import signUp from "@/api/auth/signUp";
const SignUpPage = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleSignUp = async (e: React.SyntheticEvent<HTMLFormElement>) => {
e.preventDefault();
setError(null);
if (!email || !password) {
setError(ERROR_MESSAGES.EMAIL_AND_PASSWORD_REQUIRED);
return;
}
setLoading(true);
try {
await signUp(email, password);
alert(MESSAGES.SIGN_UP_SUCCESS);
window.location.href = "/signin";
} catch (error: any) {
console.error(ERROR_MESSAGES.ERROR_IN_METHOD(getFunctionName()), error);
setError(ERROR_MESSAGES.SIGN_UP_FAILED);
} finally {
setLoading(false);
}
};
return (
<div className={`${STYLES.SIGN_PAGE}`}>
<div className={`${STYLES.SIGN_PAGE_CARD}`}>
<h1 className={`${STYLES.SIGN_PAGE_TITLE}`}>Sign Up</h1>
<form onSubmit={handleSignUp} className={`${STYLES.SIGN_FORM}`}>
<div className={`${STYLES.SIGN_FORM_LABEL_INPUT}`}>
<label className={`${STYLES.SIGN_FORM_LABEL}`} htmlFor="email">
Email
</label>
<input
id="email"
type="email"
autoComplete="email"
placeholder="you@example.com"
className={`${STYLES.SIGN_FORM_INPUT}`}
value={email}
onChange={(e) => setEmail(e.target.value)}
disabled={loading}
required
/>
</div>
<div className={`${STYLES.SIGN_FORM_LABEL_INPUT}`}>
<label className={`${STYLES.SIGN_FORM_LABEL}`} htmlFor="password">
Password
</label>
<input
id="password"
type="password"
autoComplete="new-password"
placeholder="••••••••"
className={`${STYLES.SIGN_FORM_INPUT}`}
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={loading}
required
/>
</div>
{error && <p className={`${STYLES.SIGN_FORM_ERROR}`}>{error}</p>}
<button
type="submit"
className={`${STYLES.SIGN_FORM_BUTTON}`}
disabled={loading}
>
{loading ? "Signing up…" : "Sign Up"}
</button>
</form>
<p className={`${STYLES.SIGN_FORM_HINT}`}>
既にアカウントをお持ちの方は{" "}
<a href="/signin" className={`${STYLES.LINK}`}>
Sign In
</a>{" "}
してください。
</p>
</div>
</div>
);
};
export default SignUpPage;
バックエンドサーバを起動した状態で、ブラウザで下記 URL にアクセスしてください。
例えば下記のデータを入力し、Sign Upボタンをクリックしてください。
- Email:
bar@example.com - Password:
password123
「ユーザ登録が完了しました。サインインしてください。」と表示されれば成功です。バックエンド側のデータベースにもユーザが登録されていることを確認してください。なお、サインアップ後はサインインページにリダイレクトされますが、現時点ではサインインページは作成していませんので、404のページが表示されます。
バックエンドサーバが停止している状態でサインアップすると、「サインアップに失敗しました。時間をおいて再度お試しください。」とエラーが表示されることも確認してください。