|
- import { useState } from 'react'
- import { Helmet } from 'react-helmet-async'
- import { useLocation, useNavigate } from 'react-router-dom'
-
- import Form from '@/components/common/Form'
- import Label from '@/components/common/Label'
- import PageTitle from '@/components/common/PageTitle'
- import TagInput from '@/components/common/TagInput'
- import MainArea from '@/components/layout/MainArea'
- import { Button } from '@/components/ui/button'
- import { toast } from '@/components/ui/use-toast'
- import { SITE_TITLE } from '@/config'
- import { apiPost } from '@/lib/api'
-
- import type { FC } from 'react'
-
-
- export default (() => {
- const location = useLocation ()
- const query = new URLSearchParams (location.search)
- const tagQuery = query.get ('tag') ?? ''
-
- const navigate = useNavigate ()
-
- const [file, setFile] = useState<File | null> (null)
- const [filePreview, setFilePreview] = useState ('')
- const [sending, setSending] = useState (false)
- const [tag, setTag] = useState (tagQuery)
- const [url, setURL] = useState ('')
-
- const handleSubmit = async () => {
- const formData = new FormData
- if (tag)
- formData.append ('tag', tag)
- if (file)
- formData.append ('file', file)
- if (url)
- formData.append ('url', url)
-
- try
- {
- setSending (true)
- await apiPost ('/materials', formData)
- toast ({ title: '送信成功!' })
- navigate (`/materials?tag=${ encodeURIComponent (tag) }`)
- }
- catch
- {
- toast ({ title: '送信失敗……', description: '入力を見直してください.' })
- }
- finally
- {
- setSending (false)
- }
- }
-
- return (
- <MainArea>
- <Helmet>
- <title>{`素材追加 | ${ SITE_TITLE }`}</title>
- </Helmet>
-
- <Form>
- <PageTitle>素材追加</PageTitle>
-
- {/* タグ */}
- <div>
- <Label>タグ</Label>
- <TagInput value={tag} setValue={setTag}/>
- </div>
-
- {/* ファイル */}
- <div>
- <Label>ファイル</Label>
- <input
- type="file"
- accept="image/*,video/*,audio/*"
- onChange={e => {
- const f = e.target.files?.[0]
- setFile (f ?? null)
- setFilePreview (f ? URL.createObjectURL (f) : '')
- }}/>
- {(file && filePreview) && (
- (/image\/.*/.test (file.type) && (
- <img
- src={filePreview}
- alt="preview"
- className="mt-2 max-h-48 rounded border"/>))
- || (/video\/.*/.test (file.type) && (
- <video
- src={filePreview}
- controls
- className="mt-2 max-h-48 rounded border"/>))
- || (/audio\/.*/.test (file.type) && (
- <audio
- src={filePreview}
- controls
- className="mt-2 max-h-48"/>))
- || (
- <p className="text-red-600 dark:text-red-400">
- その形式のファイルには対応していません.
- </p>))}
- </div>
-
- {/* 参考 URL */}
- <div>
- <Label>参考 URL</Label>
- <input
- type="url"
- value={url}
- onChange={e => setURL (e.target.value)}
- className="w-full border p-2 rounded"/>
- </div>
-
- {/* 送信 */}
- <Button
- onClick={handleSubmit}
- className="px-4 py-2 bg-blue-600 text-white rounded disabled:bg-gray-400"
- disabled={sending}>
- 追加
- </Button>
- </Form>
- </MainArea>)
- }) satisfies FC
|