Skip to content
GitLab
Next
Menu
Projects
Groups
Snippets
/
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
k1350
sololog_gql
Commits
19c610e8
Commit
19c610e8
authored
Jun 26, 2022
by
k1350
Browse files
[update] ブログ編集画面を react hook form に変更
parent
314d7508
Changes
6
Hide whitespace changes
Inline
Side-by-side
sololog_front/package-lock.json
View file @
19c610e8
...
...
@@ -10,6 +10,7 @@
"dependencies": {
"@chakra-ui/icons": "^2.0.2",
"@chakra-ui/react": "^2.1.2",
"@chakra-ui/skip-nav": "^2.0.2",
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@fontsource/noto-sans-jp": "^4.5.10",
...
...
@@ -23,6 +24,7 @@
"graphql": "^15.8.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-hook-form": "^7.33.0",
"react-query": "^3.39.1"
},
"devDependencies": {
...
...
@@ -1470,6 +1472,29 @@
"react": ">=18"
}
},
"node_modules/@chakra-ui/skip-nav": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@chakra-ui/skip-nav/-/skip-nav-2.0.2.tgz",
"integrity": "sha512-MbcRNcs0CYD4EUb+0xZkW8mpjGA7/z3m520LhpjdMUz2own5v9PymeaUaBp6u3uoK36X+vMp1KYlXhy71PSePg==",
"dependencies": {
"@chakra-ui/utils": "2.0.2"
},
"peerDependencies": {
"@chakra-ui/system": ">=2.0.0-next.0",
"react": ">=18"
}
},
"node_modules/@chakra-ui/skip-nav/node_modules/@chakra-ui/utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.2.tgz",
"integrity": "sha512-9AC/ir9zm0shgFG7kdzOKUH2Wx5VB71M3uRMEsMZf75YlhhiU7AvBNtWXnJu+CBiTi41rKa5A+2ImMOsuPfGbA==",
"dependencies": {
"@types/lodash.mergewith": "4.6.6",
"css-box-model": "1.2.1",
"framesync": "5.3.0",
"lodash.mergewith": "4.6.2"
}
},
"node_modules/@chakra-ui/slider": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@chakra-ui/slider/-/slider-2.0.1.tgz",
...
...
@@ -6582,6 +6607,21 @@
}
}
},
"node_modules/react-hook-form": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.33.0.tgz",
"integrity": "sha512-h8XoeUHQs1Snx1s/sSvM+eVTSKkWQt8TcrbL+3/Rt5gugxpy4ueL5ZZkubffyNpUyyTz0qM0kwOi2c+JgGTjLA==",
"engines": {
"node": ">=12.22.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/react-hook-form"
},
"peerDependencies": {
"react": "^16.8.0 || ^17 || ^18"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
...
...
@@ -8826,6 +8866,27 @@
"@chakra-ui/utils": "2.0.1"
}
},
"@chakra-ui/skip-nav": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@chakra-ui/skip-nav/-/skip-nav-2.0.2.tgz",
"integrity": "sha512-MbcRNcs0CYD4EUb+0xZkW8mpjGA7/z3m520LhpjdMUz2own5v9PymeaUaBp6u3uoK36X+vMp1KYlXhy71PSePg==",
"requires": {
"@chakra-ui/utils": "2.0.2"
},
"dependencies": {
"@chakra-ui/utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.2.tgz",
"integrity": "sha512-9AC/ir9zm0shgFG7kdzOKUH2Wx5VB71M3uRMEsMZf75YlhhiU7AvBNtWXnJu+CBiTi41rKa5A+2ImMOsuPfGbA==",
"requires": {
"@types/lodash.mergewith": "4.6.6",
"css-box-model": "1.2.1",
"framesync": "5.3.0",
"lodash.mergewith": "4.6.2"
}
}
}
},
"@chakra-ui/slider": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@chakra-ui/slider/-/slider-2.0.1.tgz",
...
...
@@ -12608,6 +12669,12 @@
"use-sidecar": "^1.1.2"
}
},
"react-hook-form": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.33.0.tgz",
"integrity": "sha512-h8XoeUHQs1Snx1s/sSvM+eVTSKkWQt8TcrbL+3/Rt5gugxpy4ueL5ZZkubffyNpUyyTz0qM0kwOi2c+JgGTjLA==",
"requires": {}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
...
...
sololog_front/package.json
View file @
19c610e8
...
...
@@ -11,6 +11,7 @@
"dependencies"
:
{
"@chakra-ui/icons"
:
"^2.0.2"
,
"@chakra-ui/react"
:
"^2.1.2"
,
"@chakra-ui/skip-nav"
:
"^2.0.2"
,
"@emotion/react"
:
"^11.9.0"
,
"@emotion/styled"
:
"^11.8.1"
,
"@fontsource/noto-sans-jp"
:
"^4.5.10"
,
...
...
@@ -24,6 +25,7 @@
"graphql"
:
"^15.8.0"
,
"react"
:
"^18.0.0"
,
"react-dom"
:
"^18.0.0"
,
"react-hook-form"
:
"^7.33.0"
,
"react-query"
:
"^3.39.1"
},
"devDependencies"
:
{
...
...
sololog_front/src/constants.ts
0 → 100644
View file @
19c610e8
export
const
CONSTANT
=
{
PUBLIC_STATUS
:
{
PUBLIC
:
"
1
"
,
PASSWORD
:
"
2
"
,
PRIVATE
:
"
99
"
,
},
MAX_LINKS
:
3
,
ERROR_MESSAGE
:
{
REQUIRED
:
"
必須入力です
"
,
MIN_LENGTH
:
(
min
:
number
)
=>
{
return
`
${
min
}
文字以上で入力してください`
;
},
MAX_LENGTH
:
(
max
:
number
)
=>
{
return
`
${
max
}
文字以内で入力してください`
;
},
PASSWORD
:
"
使用できない文字が含まれています
"
,
LINK_URL_REQUIRED
:
"
リンクタイトルを入力した場合は必須入力です
"
,
LINK_NAME_REQUIRED
:
"
URL を入力した場合は必須入力です
"
,
},
};
sololog_front/src/pages/BlogDetail.tsx
View file @
19c610e8
...
...
@@ -9,6 +9,7 @@ import {
Grid
,
Button
,
Center
,
useMediaQuery
,
}
from
"
@chakra-ui/react
"
;
import
{
useGetBlogQuery
}
from
"
../api/generated
"
;
import
dayjs
from
"
dayjs
"
;
...
...
@@ -16,6 +17,7 @@ import { LinkText } from "../ui/LinkText";
import
{
CloseIcon
,
EditIcon
}
from
"
@chakra-ui/icons
"
;
export
function
BlogDetail
()
{
const
[
isLargerThan600
]
=
useMediaQuery
(
"
(min-width: 600px)
"
);
const
{
blogId
}
=
useMatch
().
params
;
const
{
status
,
data
,
error
,
isFetching
}
=
useGetBlogQuery
({
id
:
blogId
});
...
...
@@ -35,8 +37,14 @@ export function BlogDetail() {
<
Spinner
size
=
"xl"
/>
)
:
(
<
Box
>
<
Grid
templateColumns
=
"max-content 1fr"
gap
=
{
1
}
mb
=
{
2
}
>
<
Text
mr
=
{
2
}
>
URL:
</
Text
>
<
Grid
templateColumns
=
{
isLargerThan600
?
"
max-content 1fr
"
:
"
1fr
"
}
gap
=
{
1
}
mb
=
{
2
}
>
<
Text
mr
=
{
2
}
fontWeight
=
"bold"
>
URL
</
Text
>
<
Text
>
<
Link
href
=
{
...
...
@@ -54,15 +62,25 @@ export function BlogDetail() {
/>
</
Link
>
</
Text
>
<
Text
mr
=
{
2
}
>
著者名:
</
Text
>
<
Text
mr
=
{
2
}
fontWeight
=
"bold"
>
著者名
</
Text
>
<
Text
>
{
data
?.
blogById
?.
author
}
</
Text
>
<
Text
mr
=
{
2
}
>
ブログ名:
</
Text
>
<
Text
mr
=
{
2
}
fontWeight
=
"bold"
>
ブログ名
</
Text
>
<
Text
>
{
data
?.
blogById
?.
name
}
</
Text
>
<
Text
mr
=
{
2
}
>
ブログ説明:
</
Text
>
<
Text
mr
=
{
2
}
fontWeight
=
"bold"
>
ブログ説明
</
Text
>
<
Text
>
{
data
?.
blogById
?.
description
}
</
Text
>
<
Text
mr
=
{
2
}
>
公開状態:
</
Text
>
<
Text
mr
=
{
2
}
fontWeight
=
"bold"
>
公開状態
</
Text
>
<
Text
>
{
data
?.
blogById
?.
publishOption
}
</
Text
>
<
Text
mr
=
{
2
}
>
リンク:
</
Text
>
<
Text
mr
=
{
2
}
fontWeight
=
"bold"
>
リンク
</
Text
>
<
Grid
gap
=
{
2
}
>
{
data
?.
blogById
?.
links
?.
map
((
link
)
=>
(
<
Grid
...
...
@@ -70,16 +88,22 @@ export function BlogDetail() {
templateColumns
=
"max-content 1fr"
gap
=
{
1
}
>
<
Text
mr
=
{
2
}
>
リンクタイトル:
</
Text
>
<
Text
mr
=
{
2
}
fontWeight
=
"bold"
>
リンクタイトル
</
Text
>
<
Text
>
{
link
?.
name
}
</
Text
>
<
Text
mr
=
{
2
}
>
URL:
</
Text
>
<
Text
mr
=
{
2
}
fontWeight
=
"bold"
>
URL
</
Text
>
<
Link
href
=
{
link
?.
url
}
>
<
LinkText
text
=
{
link
?.
url
??
""
}
isExternal
=
{
true
}
/>
</
Link
>
</
Grid
>
))
}
</
Grid
>
<
Text
mr
=
{
2
}
>
作成日時:
</
Text
>
<
Text
mr
=
{
2
}
fontWeight
=
"bold"
>
作成日時
</
Text
>
<
Text
>
<
time
dateTime
=
{
data
?.
blogById
?.
createdAt
}
>
{
dayjs
(
data
?.
blogById
?.
createdAt
).
format
(
...
...
@@ -87,7 +111,9 @@ export function BlogDetail() {
)
}
</
time
>
</
Text
>
<
Text
mr
=
{
2
}
>
更新日時:
</
Text
>
<
Text
mr
=
{
2
}
fontWeight
=
"bold"
>
更新日時
</
Text
>
<
Text
>
<
time
dateTime
=
{
data
?.
blogById
?.
updatedAt
}
>
{
dayjs
(
data
?.
blogById
?.
updatedAt
).
format
(
...
...
sololog_front/src/pages/BlogEdit.tsx
View file @
19c610e8
import
{
useState
,
useEffect
}
from
"
react
"
;
import
{
useMatch
}
from
"
@tanstack/react-location
"
;
import
{
useForm
}
from
"
react-hook-form
"
;
import
{
FormErrorMessage
,
FormLabel
,
FormControl
,
Heading
,
Spinner
,
ButtonGroup
,
Link
,
Box
,
Text
,
Grid
,
...
...
@@ -12,107 +15,206 @@ import {
Center
,
Input
,
Textarea
,
Radio
,
RadioGroup
,
Stack
,
FormHelperText
,
Flex
,
Select
,
InputGroup
,
InputRightElement
,
Divider
,
Checkbox
,
useMediaQuery
,
}
from
"
@chakra-ui/react
"
;
import
{
useGetBlogQuery
}
from
"
../api/generated
"
;
import
{
LinkText
}
from
"
../ui/LinkText
"
;
import
{
C
loseIcon
,
CheckIcon
}
from
"
@chakra-ui/icon
s
"
;
import
{
CheckIcon
,
CloseIcon
,
ViewIcon
,
ViewOffIcon
}
from
"
@chakra-ui/icons
"
;
import
{
C
ONSTANT
}
from
"
../constant
s
"
;
export
function
BlogEdit
()
{
const
[
isLargerThan600
]
=
useMediaQuery
(
"
(min-width: 600px)
"
);
const
gridTemplate
=
"
7em 1fr
"
;
const
{
blogId
}
=
useMatch
().
params
;
const
{
status
,
data
,
error
,
isFetching
}
=
useGetBlogQuery
({
id
:
blogId
});
const
[
author
,
setAuthor
]
=
useState
(
""
);
const
handleAuthorChange
=
(
event
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
setAuthor
(
event
.
target
.
value
);
const
[
name
,
setName
]
=
useState
(
""
);
const
handleNameChange
=
(
event
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
setName
(
event
.
target
.
value
);
const
[
description
,
setDescription
]
=
useState
(
""
);
const
handleDescriptionChange
=
(
event
:
React
.
ChangeEvent
<
HTMLTextAreaElement
>
)
=>
setDescription
(
event
.
target
.
value
);
const
[
publishOption
,
setPublishOption
]
=
useState
(
""
);
const
handlePublishOptionChange
=
(
nextValue
:
string
)
=>
setPublishOption
(
nextValue
);
const
[
links
,
setLinks
]
=
useState
(
new
Map
<
number
,
{
name
:
string
;
url
:
string
}
>
()
);
const
handleLinkChange
=
(
event
:
React
.
ChangeEvent
<
HTMLInputElement
>
,
id
:
number
,
isName
:
boolean
)
=>
{
if
(
isName
)
{
updateLinks
(
id
,
event
.
target
.
value
);
return
;
}
updateLinks
(
id
,
undefined
,
event
.
target
.
value
);
};
const
[
isInitialized
,
setIsInitialized
]
=
useState
(
false
);
const
{
handleSubmit
,
register
,
setValue
,
watch
,
formState
:
{
errors
,
isSubmitting
},
}
=
useForm
({
mode
:
"
all
"
});
const
watchPublishOption
=
watch
(
"
publishOption
"
);
const
[
showPasswordInput
,
setShowPasswordInput
]
=
useState
(
false
);
const
[
showPassword
,
setShowPassword
]
=
useState
(
false
);
const
handleClick
=
()
=>
setShowPassword
(
!
showPassword
);
const
[
hidePassword
,
setHidePassword
]
=
useState
(
true
);
const
handleHidePasswordCheck
=
(
value
:
boolean
)
=>
setHidePassword
(
value
);
const
updateLinks
=
(
id
:
number
,
name
?:
string
,
url
?:
string
)
=>
{
const
prev
=
links
.
get
(
id
);
if
(
prev
)
{
if
(
name
!==
undefined
)
{
links
.
set
(
id
,
{
...
prev
,
name
});
}
else
if
(
url
!==
undefined
)
{
links
.
set
(
id
,
{
...
prev
,
url
});
}
}
else
{
links
.
set
(
id
,
{
name
:
name
??
""
,
url
:
url
??
""
});
}
setLinks
(
new
Map
<
number
,
{
name
:
string
;
url
:
string
}
>
(
links
));
const
onSubmit
=
(
data
:
any
)
=>
{
console
.
log
(
data
);
};
useEffect
(()
=>
{
if
(
status
!==
"
success
"
)
{
return
;
}
if
(
data
?.
blogById
)
{
set
A
uthor
(
data
.
blogById
.
author
);
set
Name
(
data
.
blogById
.
name
);
set
D
escription
(
data
.
blogById
.
description
);
if
(
!
isInitialized
&&
data
?.
blogById
)
{
set
Value
(
"
a
uthor
"
,
data
.
blogById
.
author
);
set
Value
(
"
name
"
,
data
.
blogById
.
name
);
set
Value
(
"
d
escription
"
,
data
.
blogById
.
description
);
switch
(
data
.
blogById
.
publishOption
)
{
case
"
PUBLIC
"
:
set
P
ublishOption
(
"
1
"
);
set
Value
(
"
p
ublishOption
"
,
CONSTANT
.
PUBLIC_STATUS
.
PUBLIC
);
break
;
case
"
PASSWORD
"
:
set
P
ublishOption
(
"
2
"
);
set
Value
(
"
p
ublishOption
"
,
CONSTANT
.
PUBLIC_STATUS
.
PASSWORD
);
break
;
default
:
set
P
ublishOption
(
"
99
"
);
set
Value
(
"
p
ublishOption
"
,
CONSTANT
.
PUBLIC_STATUS
.
PRIVATE
);
break
;
}
for
(
let
i
=
0
;
i
<
3
;
i
++
)
{
updateLinks
(
i
,
data
.
blogById
.
links
?.[
i
]?.
name
??
""
,
data
.
blogById
.
links
?.[
i
]?.
url
??
""
);
for
(
let
i
=
0
;
i
<
CONSTANT
.
MAX_LINKS
;
i
++
)
{
setValue
(
`links
${
i
}
_name`
,
data
.
blogById
.
links
?.[
i
]?.
name
??
""
);
setValue
(
`links
${
i
}
_url`
,
data
.
blogById
.
links
?.[
i
]?.
url
??
""
);
}
}
},
[
status
]);
if
(
!
isInitialized
)
{
setIsInitialized
(
true
);
}
if
(
watchPublishOption
===
CONSTANT
.
PUBLIC_STATUS
.
PASSWORD
)
{
setShowPasswordInput
(
true
);
}
else
{
setShowPasswordInput
(
false
);
}
},
[
status
,
isInitialized
,
watchPublishOption
]);
const
passwordInput
=
()
=>
{
if
(
showPasswordInput
)
{
return
(
<
Grid
templateColumns
=
{
isLargerThan600
?
gridTemplate
:
"
1fr
"
}
gap
=
{
1
}
mb
=
{
4
}
>
<
span
></
span
>
<
Checkbox
defaultChecked
onChange
=
{
(
event
)
=>
handleHidePasswordCheck
(
event
.
target
.
checked
)
}
>
現在のパスワードと同じ
</
Checkbox
>
{
hidePassword
?
(
<></>
)
:
(
<>
<
span
></
span
>
<
FormControl
isInvalid
=
{
Boolean
(
errors
.
password
)
}
mb
=
{
2
}
>
<
FormLabel
htmlFor
=
"password"
>
<
Flex
>
<
Text
fontWeight
=
"bold"
>
パスワード
</
Text
>
<
Text
color
=
"red.600"
>
*
</
Text
>
</
Flex
>
</
FormLabel
>
<
InputGroup
size
=
"md"
>
<
Input
id
=
"password"
pr
=
"4.5rem"
type
=
{
showPassword
?
"
text
"
:
"
password
"
}
{
...
register
(
"
password
"
,
{
required
:
CONSTANT
.
ERROR_MESSAGE
.
REQUIRED
,
pattern
:
{
value
:
/^
[
a-zA-Z
\d
!@#%
\^
&
\*]
+$/i
,
message
:
CONSTANT
.
ERROR_MESSAGE
.
PASSWORD
,
},
minLength
:
{
value
:
8
,
message
:
CONSTANT
.
ERROR_MESSAGE
.
MIN_LENGTH
(
8
),
},
maxLength
:
{
value
:
255
,
message
:
CONSTANT
.
ERROR_MESSAGE
.
MAX_LENGTH
(
255
),
},
})
}
/>
<
InputRightElement
width
=
"4.5rem"
>
<
Button
h
=
"1.75rem"
size
=
"sm"
onClick
=
{
handleClick
}
>
{
showPassword
?
<
ViewOffIcon
/>
:
<
ViewIcon
/>
}
</
Button
>
</
InputRightElement
>
</
InputGroup
>
<
FormErrorMessage
>
{
errors
.
password
&&
errors
.
password
.
message
?.
toString
()
}
</
FormErrorMessage
>
<
FormHelperText
ml
=
{
4
}
>
<
ul
>
<
li
>
8 文字以上 255 文字以下
</
li
>
<
li
>
半角英数字と記号が使用できます
</
li
>
<
li
>
使用できる記号は!@#%\^
&
\*
</
li
>
</
ul
>
</
FormHelperText
>
</
FormControl
>
</>
)
}
</
Grid
>
);
}
return
(
<>
<
span
></
span
>
<
FormControl
mb
=
{
2
}
></
FormControl
>
</>
);
};
const
linksInput
=
(
links
:
Map
<
number
,
{
name
:
string
;
url
:
string
}
>
)
=>
{
const
result
:
JSX
.
Element
[]
=
[];
for
(
let
i
=
0
;
i
<
3
;
i
++
)
{
result
.
push
(
<
Grid
key
=
{
`new-
${
i
}
`
}
templateColumns
=
"max-content 1fr"
gap
=
{
1
}
>
<
Text
mr
=
{
2
}
>
リンクタイトル:
</
Text
>
const
linksInput
=
(
i
:
number
)
=>
{
const
{
[
`links
${
i
}
_name`
]:
nameError
}
=
errors
;
const
{
[
`links
${
i
}
_url`
]:
urlError
}
=
errors
;
return
(
<
div
key
=
{
`new-
${
i
}
`
}
>
<
FormControl
isInvalid
=
{
Boolean
(
nameError
)
}
mb
=
{
2
}
>
<
FormLabel
htmlFor
=
{
`link-name-
${
i
}
`
}
mr
=
{
2
}
>
<
Text
fontWeight
=
"bold"
>
リンクタイトル
</
Text
>
</
FormLabel
>
<
Input
value
=
{
links
.
get
(
i
)?.
name
??
""
}
onChange
=
{
(
e
)
=>
handleLinkChange
(
e
,
i
,
true
)
}
id
=
{
`link-name-
${
i
}
`
}
{
...
register
(
`links
${
i
}
_name`
,
{
maxLength
:
{
value
:
255
,
message
:
CONSTANT
.
ERROR_MESSAGE
.
MAX_LENGTH
(
255
),
},
})
}
></
Input
>
<
Text
mr
=
{
2
}
>
URL:
</
Text
>
{
nameError
?
(
<
FormErrorMessage
>
{
nameError
.
message
?.
toString
()
}
</
FormErrorMessage
>
)
:
(
<></>
)
}
</
FormControl
>
<
FormControl
isInvalid
=
{
Boolean
(
urlError
)
}
mb
=
{
4
}
>
<
FormLabel
htmlFor
=
{
`link-url-
${
i
}
`
}
mr
=
{
2
}
>
<
Text
fontWeight
=
"bold"
>
URL
</
Text
>
</
FormLabel
>
<
Input
value
=
{
links
.
get
(
i
)?.
url
??
""
}
onChange
=
{
(
e
)
=>
handleLinkChange
(
e
,
i
,
false
)
}
id
=
{
`link-url-
${
i
}
`
}
{
...
register
(
`links
${
i
}
_url`
,
{
maxLength
:
{
value
:
3000
,
message
:
CONSTANT
.
ERROR_MESSAGE
.
MAX_LENGTH
(
3000
),
},
validate
:
(
value
:
string
)
=>
{
if
(
watch
(
`links
${
i
}
_name`
)
!==
""
&&
value
===
""
)
{
return
CONSTANT
.
ERROR_MESSAGE
.
LINK_URL_REQUIRED
;
}
},
})
}
></
Input
>
</
Grid
>
);
}
return
result
;
{
urlError
?
(
<
FormErrorMessage
>
{
urlError
.
message
?.
toString
()
}
</
FormErrorMessage
>
)
:
(
<></>
)
}
</
FormControl
>
{
i
!==
CONSTANT
.
MAX_LINKS
-
1
?
<
Divider
mb
=
{
2
}
/>
:
<></>
}
</
div
>
);
};
return
(
...
...
@@ -130,35 +232,185 @@ export function BlogEdit() {
{
isFetching
?
(
<
Spinner
size
=
"xl"
/>
)
:
(
<
Box
>
<
Grid
templateColumns
=
"max-content 1fr"
gap
=
{
1
}
mb
=
{
2
}
>
<
Text
mr
=
{
2
}
>
著者名:
</
Text
>
<
Input
value
=
{
author
}
onChange
=
{
handleAuthorChange
}
/>
<
Text
mr
=
{
2
}
>
ブログ名:
</
Text
>
<
Input
value
=
{
name
}
onChange
=
{
handleNameChange
}
/>
<
Text
mr
=
{
2
}
>
ブログ説明:
</
Text
>
<
Textarea
value
=
{
description
}
onChange
=
{
handleDescriptionChange
}
/>
<
Text
mr
=
{
2
}
>
公開状態:
</
Text
>
<
RadioGroup
onChange
=
{
handlePublishOptionChange
}
value
=
{
publishOption
}
<
form
onSubmit
=
{
handleSubmit
(
onSubmit
)
}
>
<
Text
color
=
"red.600"
mb
=
{
2
}
>
* は必須入力
</
Text
>
<
FormControl
isInvalid
=
{
Boolean
(
errors
.
author
)
}
mb
=
{
4
}
>
<
Grid
templateColumns
=
{
isLargerThan600
?
gridTemplate
:
"
1fr
"
}
gap
=
{
1
}
mb
=
{
2
}
>
<
FormLabel
htmlFor
=
"author"
>
<
Flex
>
<
Text
fontWeight
=
"bold"
>
著者名
</
Text
>
<
Text
color
=
"red.600"
>
*
</
Text
>
</
Flex
>
</
FormLabel
>
<
Input
id
=
"author"
{
...
register
(
"
author
"
,
{
required
:
CONSTANT
.
ERROR_MESSAGE
.
REQUIRED
,
maxLength
:
{
value
:
50
,
message
:
CONSTANT
.
ERROR_MESSAGE
.
MAX_LENGTH
(
50
),
},
})
}
/>
{
errors
.
author
?
(
<>
<
span
></
span
>
<
FormErrorMessage
>
{
errors
.
author
.
message
?.
toString
()
}
</
FormErrorMessage
>
</>
)
:
(
<>
<
span
></
span
>
<
FormHelperText
>
最大 50 文字
</
FormHelperText
>
</>
)
}
</
Grid
>
</
FormControl
>
<
FormControl
isInvalid
=
{
Boolean
(
errors
.
name
)
}
mb
=
{
4
}
>
<
Grid
templateColumns
=
{
isLargerThan600
?
gridTemplate
:
"
1fr
"
}
gap
=
{
1
}
mb
=
{
2
}
>