diff options
author | Alexander Neonxp Kiryukhin <i@neonxp.ru> | 2024-10-29 01:21:05 +0300 |
---|---|---|
committer | Alexander Neonxp Kiryukhin <i@neonxp.ru> | 2024-10-29 01:21:05 +0300 |
commit | fd4e0c3112d69061d495dfcf79f6ef62e3c6d5e6 (patch) | |
tree | fa55d6e879e3c6d85601d5c2486837f323ffd81d /web/src | |
parent | b26bd10926447ed59cbf263aef087bb7c04f35eb (diff) |
Diffstat (limited to 'web/src')
-rw-r--r-- | web/src/components/message.js | 20 | ||||
-rw-r--r-- | web/src/index.css | 6 | ||||
-rw-r--r-- | web/src/index.html | 13 | ||||
-rw-r--r-- | web/src/index.js | 49 | ||||
-rw-r--r-- | web/src/pages/echo.js | 22 | ||||
-rw-r--r-- | web/src/pages/list.js | 21 | ||||
-rw-r--r-- | web/src/root.js | 10 |
7 files changed, 141 insertions, 0 deletions
diff --git a/web/src/components/message.js b/web/src/components/message.js new file mode 100644 index 0000000..92c15be --- /dev/null +++ b/web/src/components/message.js @@ -0,0 +1,20 @@ +import React from "react"; +import Markdown from "react-markdown"; +import remarkBreaks from "remark-breaks"; + +const Message = ({message}) => { + return ( + <article name={message.id}> + <header className="msg-header"> + <span>{message.subject}</span> + <span>{(new Date(message.date)).toLocaleDateString()}</span> + </header> + <Markdown remarkPlugins={[remarkBreaks]}>{message.message}</Markdown> + <pre> + {JSON.stringify(message, null, 4)} + </pre> + </article> + ); +} + +export default Message;
\ No newline at end of file diff --git a/web/src/index.css b/web/src/index.css new file mode 100644 index 0000000..66b008c --- /dev/null +++ b/web/src/index.css @@ -0,0 +1,6 @@ +@import url("@picocss/pico"); + +.msg-header { + display: flex; + justify-content: space-between; +}
\ No newline at end of file diff --git a/web/src/index.html b/web/src/index.html new file mode 100644 index 0000000..8d59bf1 --- /dev/null +++ b/web/src/index.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>IDEC Client</title> + <link rel="stylesheet" href="./index.css" /> + </head> + <body> + <div id="app">Для этого приложения необходим JavaScript</div> + </body> + <script src="./index.js"></script> +</html> diff --git a/web/src/index.js b/web/src/index.js new file mode 100644 index 0000000..a43ed75 --- /dev/null +++ b/web/src/index.js @@ -0,0 +1,49 @@ +import React from "react"; +import { createRoot } from "react-dom/client"; +import { createHashRouter, RouterProvider } from "react-router-dom"; +import Root from "./root"; +import List from "./pages/list"; +import Echo from "./pages/echo"; + +const router = createHashRouter([ + { + path: "/", + element: <Root />, + // loader: rootLoader, + children: [ + { + path: "", + element: <List />, + loader: () => { + return fetch("/api/list").then((x) => x.json()); + }, + }, + { + path: "e/:echoID", + element: <Echo />, + loader: async ({ params }) => { + const echoData = await fetch( + `/api/e?e=${params.echoID}&limit=10` + ).then((x) => x.json()); + let echo = []; + if (echoData[params.echoID]) { + echo = echoData[params.echoID]; + } + const messages = await fetch( + `/api/m?e=${params.echoID}` + ).then((x) => x.json()); + + return { echo, messages: messages.reverse() }; + }, + }, + ], + }, +]); + +const root = createRoot(document.getElementById("app")); +root.render( + <RouterProvider + router={router} + fallbackElement={<article aria-busy="true">Загрузка</article>} + /> +); diff --git a/web/src/pages/echo.js b/web/src/pages/echo.js new file mode 100644 index 0000000..6ba7e67 --- /dev/null +++ b/web/src/pages/echo.js @@ -0,0 +1,22 @@ +import React from "react"; +import { useLoaderData, useParams } from "react-router"; +import Message from "../components/message"; + +const Echo = () => { + const {echo, messages} = useLoaderData(); + const params = useParams(); + if (!echo) { + return (<article aria-busy="true">Загрузка списка сообщений</article>) + } + return ( + <> + <h1>{echo.name}</h1> + <p>Сообщений: {echo.count}</p> + {messages.map((message) => ( + <Message key={message.id} message={message} /> + ))} + </> + ); +}; + +export default Echo; diff --git a/web/src/pages/list.js b/web/src/pages/list.js new file mode 100644 index 0000000..a806622 --- /dev/null +++ b/web/src/pages/list.js @@ -0,0 +1,21 @@ +import React from "react"; +import { useLoaderData } from "react-router"; +import { Link } from "react-router-dom"; + +const List = () => { + const list = useLoaderData(); + + return list + .sort((e1, e2) => e2.count - e1.count) + .map((e) => ( + <article key={e.name}> + <header> + <Link to={`/e/${e.name}`}>{e.name}</Link> + [{e.count}] + </header> + {e.description} + </article> + )); +}; + +export default List; diff --git a/web/src/root.js b/web/src/root.js new file mode 100644 index 0000000..ce1104b --- /dev/null +++ b/web/src/root.js @@ -0,0 +1,10 @@ +import React from "react"; +import { Outlet } from "react-router"; + +const Root = () => ( + <main className="container-fluid"> + <Outlet /> + </main> +) + +export default Root;
\ No newline at end of file |