Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/#228 #230

Merged
merged 13 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions .github/workflows/backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ jobs:
permissions:
id-token: write
contents: read
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
with:
Expand All @@ -48,7 +49,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Test with pytest
run: |
pip install pytest
Expand Down Expand Up @@ -90,3 +91,18 @@ jobs:
CERTIFICATE_ARN: ${{ secrets.CERTIFICATE_ARN }}
ENVIRONMENT: ${{ needs.set-environment.outputs.environment }}
run: npx -w src/backend npm run cdk deploy --all

- name: Check if PR
id: check-pr
run: |
echo "is_pr=${{ github.event_name == 'pull_request' }}" >> $GITHUB_OUTPUT

- name: PR Agent
if: steps.check-pr.outputs.is_pr == 'true'
uses: Codium-ai/pr-agent@main
env:
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_REVIEWER.EXTRA_INSTRUCTIONS: "Please use Japanese in descriptions."
PR_DESCRIPTION.EXTRA_INSTRUCTIONS: "Please use Japanese in descriptions. Titles should have prefix of commitlint pattern such as `feat:`, `chore:`, `test:`, `fix:`, `ci:`, `docs:` etc"
PR_TITLE.EXTRA_INSTRUCTIONS: "Please use Japanese in descriptions. Titles should have prefix of commitlint pattern such as `feat:`, `chore:`, `test:`, `fix:`, `ci:`, `docs:` etc"
7,707 changes: 3,767 additions & 3,940 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions src/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,20 @@
"@fullcalendar/daygrid": "^6.1.11",
"@fullcalendar/interaction": "^6.1.14",
"@fullcalendar/react": "^6.1.11",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/themes": "^3.1.5",
"@tanstack/react-query": "^5.51.23",
"@types/react-slick": "^0.23.13",
"aws-amplify": "^5.3.18",
"buffer": "^6.0.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"dayjs": "^1.11.13",
"dompurify": "^3.2.0",
"dotenv": "^16.4.5",
"lucide-react": "^0.456.0",
"marked-react": "^2.0.0",
"react": "^18.2.0",
"react-datetime": "^3.2.0",
"react-dom": "^18.2.0",
Expand All @@ -42,6 +48,7 @@
},
"devDependencies": {
"@babel/plugin-transform-runtime": "^7.24.3",
"@types/dompurify": "^3.0.5",
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@types/react-helmet": "^6.1.11",
Expand Down
17 changes: 17 additions & 0 deletions src/frontend/src/app/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@ export const createAppRouter = (_queryClient: QueryClient) =>
return { Component: LoginRoute }
},
},
// パスワード再設定ページ
{
path: paths.auth.forgotPassword.path,
lazy: async () => {
const { ForgotPasswordRoute } = await import('./routes/auth/forgot-password')
return { Component: ForgotPasswordRoute }
},
},
// パスワード再設定ページ
{
path: paths.auth.resetPassword.path,
lazy: async () => {
const { ResetPasswordRoute } = await import('./routes/auth/reset-password')
return { Component: ResetPasswordRoute }
},
},

// アプリルート
{
path: paths.app.path,
Expand Down
18 changes: 5 additions & 13 deletions src/frontend/src/app/routes/auth/auth.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import type React from 'react'

import logo from '@/assets/logo.svg'
import { Button } from '@/features/auth/components/button'
import { AuthLayout } from '@/components/layout'
import { AuthChoice } from '@/features/auth/components/auth-choice'

export const AuthChoiceRoute: React.FC = () => {
return (
<div className="flex flex-col justify-between items-center h-screen py-12 px-5">
<div className="flex flex-grow items-center justify-center">
<img src={logo} alt="logo" className="w-32 h-32" />
</div>
<div className="w-full flex flex-col gap-6">
<Button path="/auth/terms" text="新規登録" />
<Button path="/auth/login" text="ログイン" />
</div>
</div>
<AuthLayout>
<AuthChoice />
</AuthLayout>
)
}
10 changes: 10 additions & 0 deletions src/frontend/src/app/routes/auth/forgot-password.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { AuthLayout } from '@/components/layout/auth-layout'
import { ForgotPasswordForm } from '@/features/auth/components/forgot-password-form'

export const ForgotPasswordRoute = () => {
return (
<AuthLayout title="パスワード再設定">
<ForgotPasswordForm />
</AuthLayout>
)
}
103 changes: 57 additions & 46 deletions src/frontend/src/app/routes/auth/login.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,62 @@
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'

import { Input } from '@/features/auth/components/input'

import { useAuth } from '@/lib/auth/cognito-auth'
import { AuthLayout } from '@/components/layout/auth-layout'
import { LoginForm } from '@/features/auth/components/login-form'

export const LoginRoute = () => {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [errorMessage, setErrorMessage] = useState('')

const { signIn } = useAuth()
const navigate = useNavigate()

const handleLogin = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()

const result = await signIn(username, password)

if (result.success) {
navigate('/app/diary') // ログイン成功後のリダイレクト先
} else {
setErrorMessage(result.message)
}
}

return (
<div className="flex flex-col h-full p-5 gap-5">
<h2 className="flex items-center justify-center p-20">ログイン</h2>
<form onSubmit={handleLogin} className="space-y-7">
<Input id="username" label="メールアドレス" type="text" value={username} onChange={(e) => setUsername(e.target.value)} />
<div>
<Input id="password" label="パスワード" type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
{errorMessage && <p className="pt-1 text-sm text-red-500">{errorMessage}</p>}
</div>
<div className="flex justify-center w-full text-sm text-light-textPlaceholder">
<a href="/forgot-password" className="hover:underline">
パスワードをお忘れの方はこちら
</a>
</div>
<button
type="submit"
className="w-full bg-light-buttonPrimaryDefault p-2 rounded hover:bg-light-buttonPrimaryHover transition-colors duration-200"
>
ログイン
</button>
</form>
</div>
<AuthLayout title="ログイン">
<LoginForm />
</AuthLayout>
)
}

// import { useState } from 'react'
// import { useNavigate } from 'react-router-dom'

// import { Input } from '@/features/auth/components/input'

// import { useAuth } from '@/lib/auth/cognito-auth'

// export const LoginRoute = () => {
// const [username, setUsername] = useState('')
// const [password, setPassword] = useState('')
// const [errorMessage, setErrorMessage] = useState('')

// const { signIn } = useAuth()
// const navigate = useNavigate()

// const handleLogin = async (event: React.FormEvent<HTMLFormElement>) => {
// event.preventDefault()

// const result = await signIn(username, password)

// if (result.success) {
// navigate('/app/diary') // ログイン成功後のリダイレクト先
// } else {
// setErrorMessage(result.message)
// }
// }

// return (
// <div className="flex flex-col h-full p-5 gap-5">
// <h2 className="flex items-center justify-center p-20">ログイン</h2>
// <form onSubmit={handleLogin} className="space-y-7">
// <Input id="username" label="メールアドレス" type="text" value={username} onChange={(e) => setUsername(e.target.value)} />
// <div>
// <Input id="password" label="パスワード" type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
// {errorMessage && <p className="pt-1 text-sm text-red-500">{errorMessage}</p>}
// </div>
// <div className="flex justify-center w-full text-sm text-light-textPlaceholder">
// <a href="/forgot-password" className="hover:underline">
// パスワードをお忘れの方はこちら
// </a>
// </div>
// <button
// type="submit"
// className="w-full bg-light-buttonPrimaryDefault p-2 rounded hover:bg-light-buttonPrimaryHover transition-colors duration-200"
// >
// ログイン
// </button>
// </form>
// </div>
// )
// }
90 changes: 47 additions & 43 deletions src/frontend/src/app/routes/auth/register.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,53 @@
import type React from 'react'
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { AuthLayout } from '@/components/layout'

import { useAuth } from '@/lib/auth/cognito-auth'

import { Input } from '@/features/auth/components/input'
import { RegisterForm } from '@/features/auth/components/register-form'

export const RegisterRoute = () => {
const { signUp } = useAuth() // useAuth フックから signUp 関数を取得
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [error, setError] = useState('')
const [isLoading, setIsLoading] = useState(false)
const navigate = useNavigate()

const handleSignUp = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
setIsLoading(true)
const result = await signUp(username, password)
setIsLoading(false)
if (!result.success) {
setError(result.message)
} else {
setError('')
alert('登録成功!確認コードを入力してアカウントを有効化してください。')
navigate('/auth/verify-email')
}
}

return (
<div className="flex flex-col h-full p-5 gap-5">
<h2 className="flex items-center justify-center p-20">新規登録</h2>
{error && <p className="error">{error}</p>}
<form onSubmit={handleSignUp} className="space-y-7">
<Input id="username" type="text" label="メールアドレス" value={username} onChange={(e) => setUsername(e.target.value)} />

<Input id="password" type="password" label="パスワード" value={password} onChange={(e) => setPassword(e.target.value)} />
<button
type="submit"
disabled={isLoading}
className="w-full bg-light-buttonPrimaryDefault p-2 rounded hover:bg-light-buttonPrimaryHover transition-colors duration-200"
>
{isLoading ? '登録中...' : '新規登録'}
</button>
</form>
</div>
<AuthLayout title="新規登録">
<RegisterForm />
</AuthLayout>
)
}

// export const RegisterRoute = () => {
// const { signUp } = useAuth() // useAuth フックから signUp 関数を取得
// const [username, setUsername] = useState('')
// const [password, setPassword] = useState('')
// const [error, setError] = useState('')
// const [isLoading, setIsLoading] = useState(false)
// const navigate = useNavigate()

// const handleSignUp = async (event: React.FormEvent<HTMLFormElement>) => {
// event.preventDefault()
// setIsLoading(true)
// const result = await signUp(username, password)
// setIsLoading(false)
// if (!result.success) {
// setError(result.message)
// } else {
// setError('')
// alert('登録成功!確認コードを入力してアカウントを有効化してください。')
// navigate('/auth/verify-email')
// }
// }

// return (
// <div className="flex flex-col h-full p-5 gap-5">
// <h2 className="flex items-center justify-center p-20">新規登録</h2>
// {error && <p className="error">{error}</p>}
// <form onSubmit={handleSignUp} className="space-y-7">
// <Input id="username" type="text" label="メールアドレス" value={username} onChange={(e) => setUsername(e.target.value)} />

// <Input id="password" type="password" label="パスワード" value={password} onChange={(e) => setPassword(e.target.value)} />
// <button
// type="submit"
// disabled={isLoading}
// className="w-full bg-light-buttonPrimaryDefault p-2 rounded hover:bg-light-buttonPrimaryHover transition-colors duration-200"
// >
// {isLoading ? '登録中...' : '新規登録'}
// </button>
// </form>
// </div>
// )
// }
10 changes: 10 additions & 0 deletions src/frontend/src/app/routes/auth/reset-password.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { AuthLayout } from '@/components/layout'
import { ResetPasswordForm } from '@/features/auth/components/reset-password-form'

export const ResetPasswordRoute = () => {
return (
<AuthLayout title="パスワード再設定">
<ResetPasswordForm />
</AuthLayout>
)
}
39 changes: 5 additions & 34 deletions src/frontend/src/app/routes/auth/terms.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,10 @@
import type React from 'react'
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'

import ReactMarkdown from 'react-markdown'

import { termsOfService } from '@/assets/termsOfService.ts'
import { DisabledButton } from '@/components/Elements/Button'
import { AuthLayout } from '@/components/layout'
import { TermsCheck } from '@/features/auth/components/terms-check'

export const TermsRoute: React.FC = () => {
const [isChecked, setIsChecked] = useState(false)
const navigate = useNavigate()

const handleCheckboxChange = () => {
setIsChecked(!isChecked)
}

const handleSubmit = () => {
if (isChecked) {
navigate('/auth/register')
}
}

return (
<div className="flex flex-col h-screen overflow-hidden py-12 px-5 gap-4">
<h2 className="text-xl text-center">利用規約</h2>
<div className="bg-white p-4 rounded-lg shadow h-full overflow-y-auto">
<ReactMarkdown className="prose prose-sm max-w-none">{termsOfService}</ReactMarkdown>
</div>
<div className="flex items-center justify-center gap-2">
<input type="checkbox" id="consent-checkbox" checked={isChecked} onChange={handleCheckboxChange} className="w-4 h-4" />
<label htmlFor="consent-checkbox" className="text-sm">
利用規約に同意する
</label>
</div>
<DisabledButton text="同意する" onClick={handleSubmit} disabled={!isChecked} />
</div>
<AuthLayout title="同意書">
<TermsCheck />
</AuthLayout>
)
}
Loading