summaryrefslogtreecommitdiff
path: root/web/src
diff options
context:
space:
mode:
authorAlexander Neonxp Kiryukhin <i@neonxp.ru>2024-10-29 01:21:05 +0300
committerAlexander Neonxp Kiryukhin <i@neonxp.ru>2024-10-29 01:21:05 +0300
commitfd4e0c3112d69061d495dfcf79f6ef62e3c6d5e6 (patch)
treefa55d6e879e3c6d85601d5c2486837f323ffd81d /web/src
parentb26bd10926447ed59cbf263aef087bb7c04f35eb (diff)
Начальный веб клиентHEADmaster
Diffstat (limited to 'web/src')
-rw-r--r--web/src/components/message.js20
-rw-r--r--web/src/index.css6
-rw-r--r--web/src/index.html13
-rw-r--r--web/src/index.js49
-rw-r--r--web/src/pages/echo.js22
-rw-r--r--web/src/pages/list.js21
-rw-r--r--web/src/root.js10
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>
+ &nbsp; [{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