diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0660f32..151e747 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -21,7 +21,7 @@ "markdown-it": "^14.1.0", "react": "^19.1.0", "react-dom": "^19.1.0", - "react-helmet": "^6.1.0", + "react-helmet-async": "^2.0.5", "react-markdown": "^10.1.0", "react-markdown-editor-lite": "^1.3.4", "react-router-dom": "^6.30.0", @@ -3841,6 +3841,15 @@ "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", "license": "MIT" }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", @@ -4964,6 +4973,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5338,17 +5348,6 @@ "node": ">= 0.8.0" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, "node_modules/property-information": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", @@ -5444,27 +5443,20 @@ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", "license": "MIT" }, - "node_modules/react-helmet": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", - "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", - "license": "MIT", + "node_modules/react-helmet-async": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-2.0.5.tgz", + "integrity": "sha512-rYUYHeus+i27MvFE+Jaa4WsyBKGkL6qVgbJvSBoX8mbsWoABJXdEO0bZyi0F6i+4f0NuIb8AvqPMj3iXFHkMwg==", + "license": "Apache-2.0", "dependencies": { - "object-assign": "^4.1.1", - "prop-types": "^15.7.2", - "react-fast-compare": "^3.1.1", - "react-side-effect": "^2.1.0" + "invariant": "^2.2.4", + "react-fast-compare": "^3.2.2", + "shallowequal": "^1.1.0" }, "peerDependencies": { - "react": ">=16.3.0" + "react": "^16.6.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, "node_modules/react-markdown": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", @@ -5596,15 +5588,6 @@ "react-dom": ">=16.8" } }, - "node_modules/react-side-effect": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", - "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==", - "license": "MIT", - "peerDependencies": { - "react": "^16.3.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-style-singleton": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", @@ -5805,6 +5788,12 @@ "semver": "bin/semver.js" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index eebd11e..f808241 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,7 +22,7 @@ "markdown-it": "^14.1.0", "react": "^19.1.0", "react-dom": "^19.1.0", - "react-helmet": "^6.1.0", + "react-helmet-async": "^2.0.5", "react-markdown": "^10.1.0", "react-markdown-editor-lite": "^1.3.4", "react-router-dom": "^6.30.0", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index fdcc94f..ba1a5bd 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -27,57 +27,54 @@ export default () => { const [user, setUser] = useState (null) useEffect (() => { - const createUser = () => ( - axios.post (`${ API_BASE_URL }/users`) - .then (res => { - if (res.data.code) - { - localStorage.setItem ('user_code', res.data.code) - setUser (toCamel (res.data.user, { deep: true })) - } - })) + const createUser = async () => { + const { data } = await axios.post (`${ API_BASE_URL }/users`) + if (data.code) + { + localStorage.setItem ('user_code', data.code) + setUser (toCamel (data.user, { deep: true })) + } + } const code = localStorage.getItem ('user_code') if (code) { - void (axios.post (`${ API_BASE_URL }/users/verify`, { code }) - .then (res => { - if (res.data.valid) - setUser (toCamel (res.data.user, { deep: true })) - else - createUser () - })) + void (async () => { + const { data } = await axios.post (`${ API_BASE_URL }/users/verify`, { code }) + if (data.valid) + setUser (toCamel (data.user, { deep: true })) + else + createUser () + }) () } else createUser () - - alert ('このサイトはまだ作りかけです!!!!\n出てけ!!!!!!!!!!!!!!!!!!!!') }, []) return ( <> - -
- -
- - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - -
-
-
- + +
+ +
+ + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +
+
+
+ ) } diff --git a/frontend/src/components/TagDetailSidebar.tsx b/frontend/src/components/TagDetailSidebar.tsx index e004462..a1d1222 100644 --- a/frontend/src/components/TagDetailSidebar.tsx +++ b/frontend/src/components/TagDetailSidebar.tsx @@ -47,7 +47,7 @@ export default ({ post }: Props) => { {CATEGORIES.map ((cat: Category) => cat in tags && ( -
+
{categoryNames[cat]}
    {tags[cat].map (tag => ( diff --git a/frontend/src/components/TagSidebar.tsx b/frontend/src/components/TagSidebar.tsx index e0c2f63..2a6444e 100644 --- a/frontend/src/components/TagSidebar.tsx +++ b/frontend/src/components/TagSidebar.tsx @@ -32,18 +32,18 @@ export default ({ posts }: Props) => { loop: for (const post of posts) { - for (const tag of post.tags) - { - if (!(tag.category in tagsTmp)) - tagsTmp[tag.category] = [] - if (!(tagsTmp[tag.category].map (t => t.id).includes (tag.id))) - { - tagsTmp[tag.category].push (tag) - ++cnt - if (cnt >= 25) - break loop - } - } + for (const tag of post.tags) + { + if (!(tag.category in tagsTmp)) + tagsTmp[tag.category] = [] + if (!(tagsTmp[tag.category].map (t => t.id).includes (tag.id))) + { + tagsTmp[tag.category].push (tag) + ++cnt + if (cnt >= 25) + break loop + } + } } for (const cat of Object.keys (tagsTmp)) tagsTmp[cat].sort ((tagA, tagB) => tagA.name < tagB.name ? -1 : 1) @@ -52,40 +52,38 @@ export default ({ posts }: Props) => { return ( - - タグ -
      - {CATEGORIES.map (cat => cat in tags && ( - <> - {tags[cat].map (tag => ( -
    • - - {tag.name} - - {tag.postCount} -
    • ))} - ))} -
    - 関聯 - {posts.length && ( - { - ev.preventDefault () - void ((async () => { - try - { - const { data } = await axios.get (`${ API_BASE_URL }/posts/random`, - { params: { tags: tagsQuery.split (' ').filter (e => e !== '').join (','), - match: (anyFlg ? 'any' : 'all') } }) - navigate (`/posts/${ (data as Post).id }`) - } - catch - { - ; - } - }) ()) - }}> - ランダム - )} + + タグ +
      + {CATEGORIES.flatMap (cat => cat in tags ? ( + tags[cat].map (tag => ( +
    • + + {tag.name} + + {tag.postCount} +
    • ))) : [])} +
    + 関聯 + {posts.length && ( + { + ev.preventDefault () + void ((async () => { + try + { + const { data } = await axios.get (`${ API_BASE_URL }/posts/random`, + { params: { tags: tagsQuery.split (' ').filter (e => e !== '').join (','), + match: (anyFlg ? 'any' : 'all') } }) + navigate (`/posts/${ (data as Post).id }`) + } + catch + { + ; + } + }) ()) + }}> + ランダム + )}
    ) } diff --git a/frontend/src/components/users/InheritDialogue.tsx b/frontend/src/components/users/InheritDialogue.tsx new file mode 100644 index 0000000..8642cb5 --- /dev/null +++ b/frontend/src/components/users/InheritDialogue.tsx @@ -0,0 +1,21 @@ +import { Button } from '@/components/ui/button' +import { Dialog, + DialogContent, + DialogDescription, + DialogTitle, + DialogTrigger } from '@/components/ui/dialog' + +type Props = { visible: boolean + onVisibleChange: (visible: boolean) => void + user: User | null, + setUser: (user: User) => void } + + +export default ({ visible, onVisibleChange, user, setUser }: Props) => { + return ( + + + ほかのブラウザから引継ぐ + + ) +} diff --git a/frontend/src/components/users/UserCodeDialogue.tsx b/frontend/src/components/users/UserCodeDialogue.tsx new file mode 100644 index 0000000..3f30a77 --- /dev/null +++ b/frontend/src/components/users/UserCodeDialogue.tsx @@ -0,0 +1,32 @@ +import { Button } from '@/components/ui/button' +import { Dialog, + DialogContent, + DialogDescription, + DialogTitle, + DialogTrigger } from '@/components/ui/dialog' + +type Props = { visible: boolean + onVisibleChange: (visible: boolean) => void + user: User | null } + + +export default ({ visible, onVisibleChange, user }: Props) => { + return ( + + + 引継ぎコード +
    +

    あなたの引継ぎコードはこちらです:

    +
    {user?.inheritanceCode}
    +

    + このコードはほかの人には教えないでください! +

    +
    + +
    +
    +
    +
    ) +} diff --git a/frontend/src/consts.ts b/frontend/src/consts.ts index 4b3e57b..4b06061 100644 --- a/frontend/src/consts.ts +++ b/frontend/src/consts.ts @@ -7,3 +7,7 @@ export const CATEGORIES = ['general', 'meta'] as const export const USER_ROLES = ['admin', 'member', 'guest'] as const + +export const enum ViewFlagBehavior { OnShowedDetail = 1, + OnClickedLink = 2, + NotAuto = 3 } diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index bef5202..8a8a5ae 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,10 +1,15 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.tsx' +import { HelmetProvider } from 'react-helmet-async' -createRoot(document.getElementById('root')!).render( - - - , -) +import '@/index.css' +import App from '@/App' + +const helmetContext = { } + +createRoot (document.getElementById ('root')!).render ( + + + + + ) diff --git a/frontend/src/pages/NotFound.tsx b/frontend/src/pages/NotFound.tsx index d72df89..cefa2c9 100644 --- a/frontend/src/pages/NotFound.tsx +++ b/frontend/src/pages/NotFound.tsx @@ -1,4 +1,4 @@ -import { Helmet } from 'react-helmet' +import { Helmet } from 'react-helmet-async' import MainArea from '@/components/layout/MainArea' import { SITE_TITLE } from '@/config' diff --git a/frontend/src/pages/posts/PostDetailPage.tsx b/frontend/src/pages/posts/PostDetailPage.tsx index 908dc9e..555e6d3 100644 --- a/frontend/src/pages/posts/PostDetailPage.tsx +++ b/frontend/src/pages/posts/PostDetailPage.tsx @@ -1,7 +1,7 @@ import axios from 'axios' import toCamel from 'camelcase-keys' import React, { useEffect, useState } from 'react' -import { Helmet } from 'react-helmet' +import { Helmet } from 'react-helmet-async' import { Link, useLocation, useParams } from 'react-router-dom' import TagDetailSidebar from '@/components/TagDetailSidebar' diff --git a/frontend/src/pages/posts/PostListPage.tsx b/frontend/src/pages/posts/PostListPage.tsx index 3debc34..fd4d184 100644 --- a/frontend/src/pages/posts/PostListPage.tsx +++ b/frontend/src/pages/posts/PostListPage.tsx @@ -1,7 +1,7 @@ import axios from 'axios' import toCamel from 'camelcase-keys' import React, { useEffect, useRef, useState } from 'react' -import { Helmet } from 'react-helmet' +import { Helmet } from 'react-helmet-async' import { Link, useLocation } from 'react-router-dom' import TagSidebar from '@/components/TagSidebar' @@ -14,7 +14,7 @@ import type { Post, Tag, WikiPage } from '@/types' export default () => { - const [posts, setPosts] = useState (null) + const [posts, setPosts] = useState ([]) const [wikiPage, setWikiPage] = useState (null) const [cursor, setCursor] = useState ('') const [loading, setLoading] = useState (false) @@ -24,11 +24,11 @@ export default () => { const loadMore = async withCursor => { setLoading (true) const res = await axios.get (`${ API_BASE_URL }/posts`, { - params: { tags: tags.join (' '), - match: (anyFlg ? 'any' : 'all'), - ...(withCursor ? { cursor } : { }) } }) + params: { tags: tags.join (' '), + match: (anyFlg ? 'any' : 'all'), + ...(withCursor ? { cursor } : { }) } }) const data = toCamel (res.data, { deep: true }) - setPosts (posts => [...(posts || []), ...data.posts]) + setPosts (posts => [...(withCursor ? posts : []), ...data.posts]) setCursor (data.nextCursor) setLoading (false) } @@ -42,7 +42,7 @@ export default () => { useEffect(() => { const observer = new IntersectionObserver (entries => { if (entries[0].isIntersecting && !(loading) && cursor) - loadMore (true) + loadMore (true) }, { threshold: 1 }) const target = loaderRef.current @@ -52,58 +52,65 @@ export default () => { }, [loaderRef, loading]) useEffect (() => { - setPosts (null) - setCursor ('') + setPosts ([]) loadMore (false) setWikiPage (null) if (tags.length === 1) { - void (axios.get (`${ API_BASE_URL }/wiki/title/${ encodeURIComponent (tags[0]) }`) - .then (res => setWikiPage (toCamel (res.data, { deep: true }))) - .catch (() => { - ; - })) + void (async () => { + try + { + const tagName = tags[0] + const { data } = await axios.get (`${ API_BASE_URL }/wiki/title/${ tagName }`) + setWikiPage (toCamel (data, { deep: true })) + } + catch + { + ; + } + }) () } }, [location.search]) return ( <> - - - {tags.length - ? `${ tags.join (anyFlg ? ' or ' : ' and ') } | ${ SITE_TITLE }` - : `${ SITE_TITLE } 〜 ぼざクリ関聯綜合リンク集サイト`} - - - - - - - {posts == null ? 'Loading...' : ( - posts.length - ? ( -
    - {posts.map (post => ( - - {post.title - ))} -
    ) - : '広場には何もありませんよ.')} -
    -
    - {(wikiPage && wikiPage.body) && ( - - -
    - Wiki を見る -
    -
    )} -
    -
    + + + {tags.length + ? `${ tags.join (anyFlg ? ' or ' : ' and ') } | ${ SITE_TITLE }` + : `${ SITE_TITLE } 〜 ぼざクリ関聯綜合リンク集サイト`} + + + + + + + {posts.length + ? ( +
    + {posts.map (post => ( + + {post.title + ))} +
    ) + : !(loading) && '広場には何もありませんよ.'} + {loading && 'Loading...'} +
    +
    + {(wikiPage && wikiPage.body) && ( + + +
    + Wiki を見る +
    +
    )} +
    +
    ) } diff --git a/frontend/src/pages/posts/PostNewPage.tsx b/frontend/src/pages/posts/PostNewPage.tsx index 8fd69a1..c20de45 100644 --- a/frontend/src/pages/posts/PostNewPage.tsx +++ b/frontend/src/pages/posts/PostNewPage.tsx @@ -1,6 +1,6 @@ import axios from 'axios' import React, { useEffect, useState, useRef } from 'react' -import { Helmet } from 'react-helmet' +import { Helmet } from 'react-helmet-async' import { Link, useLocation, useParams, useNavigate } from 'react-router-dom' import NicoViewer from '@/components/NicoViewer' @@ -49,8 +49,8 @@ export default () => { toast ({ title: '投稿成功!' }) navigate ('/posts') }) - .catch (e => toast ({ title: '投稿失敗', - description: '入力を確認してください。' }))) + .catch (() => toast ({ title: '投稿失敗', + description: '入力を確認してください。' }))) } useEffect (() => { diff --git a/frontend/src/pages/users/SettingPage.tsx b/frontend/src/pages/users/SettingPage.tsx index 42fa4a9..6418ef0 100644 --- a/frontend/src/pages/users/SettingPage.tsx +++ b/frontend/src/pages/users/SettingPage.tsx @@ -1,50 +1,103 @@ import { useEffect, useState } from 'react' -import { Helmet } from 'react-helmet' +import { Helmet } from 'react-helmet-async' import Form from '@/components/common/Form' import Label from '@/components/common/Label' import PageTitle from '@/components/common/PageTitle' import MainArea from '@/components/layout/MainArea' -import { SITE_TITLE } from '@/config' +import InheritDialogue from '@/components/users/InheritDialogue' +import UserCodeDialogue from '@/components/users/UserCodeDialogue' +import { API_BASE_URL, SITE_TITLE } from '@/config' +import { Button } from '@/components/ui/button' import type { User } from '@/types' -type Props = { user: User - setUser: (user: User) => void } +type Props = { user: User | null + setUser: (user: User) => void } export default ({ user, setUser }: Props) => { const [name, setName] = useState ('') + const [userCodeVsbl, setUserCodeVsbl] = useState (false) + const [inheritVsbl, setInheritVsbl] = useState (false) + + const handleSubmit = async () => { + const formData = new FormData () + formData.append ('name', name) + + try + { + await axios.post (`${ API_BASE_URL }/users`, formData, { headers: { + 'Content-Type': 'multipart/form-data', + 'X-Transfer-Code': localStorage.getItem ('user_code') || '' } }) + toast ({ title: '設定を更新しました.' }) + } + catch + { + toast ({ title: 'しっぱい……' }) + } + } useEffect (() => { if (!user) return - setName (user?.name) + setName (user.name) }, [user]) return ( - - - 設定 | {SITE_TITLE} - -
    - 設定 - - {/* 名前 */} -
    - - setName (ev.target.value)} /> - {(user && !(user.name)) && ( -

    - 名前が未設定のアカウントは 30 日間アクセスしないと削除されます!!!! -

    )} -
    -
    + + + 設定 | {SITE_TITLE} + + +
    + 設定 + + {/* 名前 */} +
    + + setName (ev.target.value)} /> + {(user && !(user.name)) && ( +

    + 名前が未設定のアカウントは 30 日間アクセスしないと削除されます!!!! +

    )} +
    + + {/* 送信 */} + + + {/* 引継ぎ */} +
    + + + +
    +
    + + + +
    ) } diff --git a/frontend/src/pages/wiki/WikiDetailPage.tsx b/frontend/src/pages/wiki/WikiDetailPage.tsx index 3c680b6..ae29823 100644 --- a/frontend/src/pages/wiki/WikiDetailPage.tsx +++ b/frontend/src/pages/wiki/WikiDetailPage.tsx @@ -1,7 +1,7 @@ import axios from 'axios' import toCamel from 'camelcase-keys' import { useEffect, useState } from 'react' -import { Helmet } from 'react-helmet' +import { Helmet } from 'react-helmet-async' import { Link, useLocation, useNavigate, useParams } from 'react-router-dom' import WikiBody from '@/components/WikiBody' diff --git a/frontend/src/pages/wiki/WikiDiffPage.tsx b/frontend/src/pages/wiki/WikiDiffPage.tsx index 0d8160f..2099433 100644 --- a/frontend/src/pages/wiki/WikiDiffPage.tsx +++ b/frontend/src/pages/wiki/WikiDiffPage.tsx @@ -1,7 +1,7 @@ import axios from 'axios' import toCamel from 'camelcase-keys' import { useEffect, useState } from 'react' -import { Helmet } from 'react-helmet' +import { Helmet } from 'react-helmet-async' import { Link, useLocation, useParams } from 'react-router-dom' import PageTitle from '@/components/common/PageTitle' diff --git a/frontend/src/pages/wiki/WikiEditPage.tsx b/frontend/src/pages/wiki/WikiEditPage.tsx index 0980936..6b5a9b8 100644 --- a/frontend/src/pages/wiki/WikiEditPage.tsx +++ b/frontend/src/pages/wiki/WikiEditPage.tsx @@ -1,7 +1,7 @@ import axios from 'axios' import MarkdownIt from 'markdown-it' import React, { useEffect, useState, useRef } from 'react' -import { Helmet } from 'react-helmet' +import { Helmet } from 'react-helmet-async' import MdEditor from 'react-markdown-editor-lite' import { Link, useLocation, useParams, useNavigate } from 'react-router-dom' diff --git a/frontend/src/pages/wiki/WikiHistoryPage.tsx b/frontend/src/pages/wiki/WikiHistoryPage.tsx index 409de81..fe9fac8 100644 --- a/frontend/src/pages/wiki/WikiHistoryPage.tsx +++ b/frontend/src/pages/wiki/WikiHistoryPage.tsx @@ -1,7 +1,7 @@ import axios from 'axios' import toCamel from 'camelcase-keys' import { useEffect, useState } from 'react' -import { Helmet } from 'react-helmet' +import { Helmet } from 'react-helmet-async' import { Link, useLocation, useParams } from 'react-router-dom' import MainArea from '@/components/layout/MainArea' diff --git a/frontend/src/pages/wiki/WikiNewPage.tsx b/frontend/src/pages/wiki/WikiNewPage.tsx index 68eb0fb..ffd3b0e 100644 --- a/frontend/src/pages/wiki/WikiNewPage.tsx +++ b/frontend/src/pages/wiki/WikiNewPage.tsx @@ -1,7 +1,7 @@ import axios from 'axios' import MarkdownIt from 'markdown-it' import React, { useEffect, useState, useRef } from 'react' -import { Helmet } from 'react-helmet' +import { Helmet } from 'react-helmet-async' import MdEditor from 'react-markdown-editor-lite' import { Link, useLocation, useParams, useNavigate } from 'react-router-dom' diff --git a/frontend/src/pages/wiki/WikiSearchPage.tsx b/frontend/src/pages/wiki/WikiSearchPage.tsx index 41243d7..b18edfe 100644 --- a/frontend/src/pages/wiki/WikiSearchPage.tsx +++ b/frontend/src/pages/wiki/WikiSearchPage.tsx @@ -1,7 +1,7 @@ import axios from 'axios' import toCamel from 'camelcase-keys' import React, { useEffect, useState } from 'react' -import { Helmet } from 'react-helmet' +import { Helmet } from 'react-helmet-async' import { Link } from 'react-router-dom' import SectionTitle from '@/components/common/SectionTitle' import MainArea from '@/components/layout/MainArea' diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx new file mode 100644 index 0000000..5cec375 --- /dev/null +++ b/frontend/src/router.tsx @@ -0,0 +1,5 @@ +import { Route, + createBrowserRouter, + createRoutesFromElements } from 'react-router-dom' + +import App from '@/App' diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 8e01fc6..acddd60 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -2,15 +2,17 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import path from 'path' + // https://vite.dev/config/ -export default defineConfig({ - plugins: [react()], - resolve: { alias: { '@': path.resolve (__dirname, './src') } }, - server: { host: true, - port: 5173, - strictPort: true, - allowedHosts: ['hub.nizika.monster', 'localhost'], - proxy: { '/api': { target: 'http://localhost:3002', - changeOrigin: true, - secure: false } } } -}) +export default defineConfig ({ + plugins: [react()], + resolve: { alias: { '@': path.resolve (__dirname, './src') } }, + server: { host: true, + port: 5173, + strictPort: true, + allowedHosts: ['hub.nizika.monster', 'localhost'], + proxy: { '/api': { target: 'http://localhost:3002', + changeOrigin: true, + secure: false } }, + watch: { usePolling: true, + interval: 100 } } })