Skip to content

Commit 98aa7ec

Browse files
authored
Merge pull request #37 from MH4GF/replace-to-route-handler
fix: replace to route handler
2 parents b7fe249 + 4dc03e2 commit 98aa7ec

File tree

6 files changed

+82
-43
lines changed

6 files changed

+82
-43
lines changed

web/.eslintrc.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,20 @@
22
module.exports = {
33
root: true,
44
extends: ['@mh4gf/eslint-config', 'next/core-web-vitals', 'plugin:tailwindcss/recommended'],
5+
overrides: [
6+
{
7+
files: ['**/*.ts', '**/*.tsx'],
8+
parserOptions: { project: './tsconfig.json' },
9+
rules: {
10+
'@typescript-eslint/no-misused-promises': [
11+
2,
12+
{
13+
checksVoidReturn: {
14+
attributes: false,
15+
},
16+
},
17+
],
18+
},
19+
},
20+
],
521
}

web/src/app/_features/GitHubNippouMain/GitHubNippouForm.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
import type { FC } from 'react'
1+
import type { FC, FormEventHandler } from 'react'
22

33
import { DateInput } from './DateInput'
44
import { GistIdInput } from './GistIdInput'
55
import { SubmitButton } from './SubmitButton'
66

77
interface Props {
8-
formAction: (formData: FormData) => void
8+
formAction: FormEventHandler<HTMLFormElement>
9+
isLoading: boolean
910
}
1011

11-
export const GitHubNippouForm: FC<Props> = ({ formAction }) => {
12+
export const GitHubNippouForm: FC<Props> = ({ formAction, isLoading }) => {
1213
return (
13-
<form action={formAction} className="grid gap-2">
14+
<form onSubmit={formAction} className="grid gap-2">
1415
<details>
1516
<summary>Advanced Settings</summary>
1617
<div className="grid gap-4">
@@ -22,7 +23,7 @@ export const GitHubNippouForm: FC<Props> = ({ formAction }) => {
2223
</div>
2324
</details>
2425
<div>
25-
<SubmitButton />
26+
<SubmitButton isLoading={isLoading} />
2627
</div>
2728
</form>
2829
)

web/src/app/_features/GitHubNippouMain/GitHubNippouMain.tsx

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,59 @@
1-
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
2-
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
3-
// useFormStateはClient Componentでしか使えない
1+
// useCallback, useStateはClient Componentでしか使えない
42
'use client'
53

6-
import type { FC } from 'react'
7-
import { useFormState } from 'react-dom'
4+
import { useState, useCallback } from 'react'
5+
import type { FormEvent, FC } from 'react'
86

97
import { CopyToClipboardButton } from '../CopyToClipboardButton'
108

119
import { GitHubNippouForm } from './GitHubNippouForm'
1210

1311
import { Alert, Label, Textarea } from '@/app/_components'
14-
import { showList } from '@/app/showList'
12+
import type { NippouResult } from '@/app/types'
1513

1614
export const GitHubNippouMain: FC = () => {
17-
const [state, formAction] = useFormState(showList, {
15+
const [state, setState] = useState<NippouResult>({
1816
success: true,
1917
result: '',
2018
})
19+
const [isLoading, setIsLoading] = useState(false)
20+
21+
const formAction = useCallback(async (event: FormEvent<HTMLFormElement>) => {
22+
setIsLoading(true)
23+
event.preventDefault()
24+
const formData = new FormData(event.currentTarget)
25+
const url = new URL('/api/nippou', window.location.href)
26+
url.searchParams.set('settingsGistId', formData.get('settingsGistId') as string)
27+
url.searchParams.set('sinceDate', formData.get('sinceDate') as string)
28+
url.searchParams.set('untilDate', formData.get('untilDate') as string)
29+
await fetch(url)
30+
.then(async (res) => {
31+
setState((await res.json()) as NippouResult)
32+
})
33+
.catch((error: unknown) => {
34+
console.error(error)
35+
setState({ success: false, error: 'An unexpected error has occurred' })
36+
})
37+
38+
setIsLoading(false)
39+
40+
return
41+
}, [])
2142

2243
return (
2344
<main className="mx-auto grid max-w-7xl gap-6 p-4 sm:p-6 lg:px-8">
24-
{state?.success === false && <Alert>{state.error}</Alert>}
25-
<GitHubNippouForm formAction={formAction} />
45+
{!state.success && <Alert>{state.error}</Alert>}
46+
<GitHubNippouForm formAction={formAction} isLoading={isLoading} />
2647
<div>
2748
<Label htmlFor="result">Result</Label>
2849
<Textarea
2950
name="result"
3051
id="result"
3152
rows={20}
32-
defaultValue={state?.success ? state.result : ''}
53+
defaultValue={state.success ? state.result : ''}
3354
/>
3455
<div className="mt-2">
35-
<CopyToClipboardButton text={state?.success ? state.result : ''} />
56+
<CopyToClipboardButton text={state.success ? state.result : ''} />
3657
</div>
3758
</div>
3859
</main>

web/src/app/_features/GitHubNippouMain/SubmitButton.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
// useFormStatusはClient Componentでしか動作しない
2-
'use client'
3-
41
import type { FC } from 'react'
5-
import { useFormStatus } from 'react-dom'
62

73
import { Button } from '@/app/_components'
84

9-
export const SubmitButton: FC = () => {
10-
const { pending } = useFormStatus()
5+
interface Props {
6+
isLoading: boolean
7+
}
118

9+
export const SubmitButton: FC<Props> = ({ isLoading }) => {
1210
return (
13-
<Button isLoading={pending} type="submit">
11+
<Button isLoading={isLoading} type="submit">
1412
Run
1513
</Button>
1614
)
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
'use server'
2-
1+
import type { NextRequest } from 'next/server'
2+
import { NextResponse } from 'next/server'
33
import { object, parse, regex, safeParse, string } from 'valibot'
44

5-
import { getServerSession } from './_auth/getServerSession'
5+
import { getServerSession } from '@/app/_auth/getServerSession'
6+
import type { NippouResult } from '@/app/types'
67

78
const dateRegex = /^\d{4}-\d{2}-\d{2}$/
89
const noHyphens = (str: string) => str.replace(/-/g, '')
@@ -19,27 +20,18 @@ const responseSchema = object({
1920
result: string(),
2021
})
2122

22-
type Result =
23-
| {
24-
success: true
25-
result: string
26-
}
27-
| {
28-
success: false
29-
error: string
30-
}
31-
32-
export const showList = async (_prevState: Result, formData: FormData): Promise<Result> => {
23+
export async function GET(req: NextRequest) {
24+
const searchParams = req.nextUrl.searchParams
3325
const session = await getServerSession()
3426
const parsed = safeParse(paramsSchema, {
3527
user: session?.user.login,
3628
token: session?.user.accessToken,
37-
settingsGistId: formData.get('settingsGistId'),
38-
sinceDate: formData.get('sinceDate'),
39-
untilDate: formData.get('untilDate'),
29+
settingsGistId: searchParams.get('settingsGistId'),
30+
sinceDate: searchParams.get('sinceDate'),
31+
untilDate: searchParams.get('untilDate'),
4032
})
4133
if (!parsed.success) {
42-
return { success: false, error: 'Invalid credentials' }
34+
return NextResponse.json({ success: false, error: 'Invalid credentials' })
4335
}
4436
const { user, token, settingsGistId, sinceDate, untilDate } = parsed.output
4537

@@ -50,11 +42,11 @@ export const showList = async (_prevState: Result, formData: FormData): Promise<
5042
url.searchParams.set('since_date', noHyphens(sinceDate))
5143
url.searchParams.set('until_date', noHyphens(untilDate))
5244

53-
return fetch(url)
45+
const result: NippouResult = await fetch(url)
5446
.then(async (res) => {
5547
if (!res.ok) {
5648
const body = await res.text()
57-
throw new Error(body)
49+
return { success: false, error: body } as const
5850
}
5951

6052
const { result } = parse(responseSchema, await res.json())
@@ -67,4 +59,6 @@ export const showList = async (_prevState: Result, formData: FormData): Promise<
6759
}
6860
return { success: false, error: 'An unexpected error has occurred' }
6961
})
62+
63+
return NextResponse.json(result)
7064
}

web/src/app/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export type NippouResult =
2+
| {
3+
success: true
4+
result: string
5+
}
6+
| {
7+
success: false
8+
error: string
9+
}

0 commit comments

Comments
 (0)