diff options
Diffstat (limited to 'web')
| -rw-r--r-- | web/.gitignore | 24 | ||||
| -rw-r--r-- | web/.vscode/extensions.json | 4 | ||||
| -rw-r--r-- | web/.vscode/launch.json | 11 | ||||
| -rw-r--r-- | web/Dockerfile | 9 | ||||
| -rw-r--r-- | web/astro.config.mjs | 21 | ||||
| -rw-r--r-- | web/bun.lockb | bin | 0 -> 179198 bytes | |||
| -rw-r--r-- | web/package.json | 20 | ||||
| -rw-r--r-- | web/public/favicon.svg | 9 | ||||
| -rw-r--r-- | web/src/components/Quotes.jsx | 88 | ||||
| -rw-r--r-- | web/src/layouts/Layout.astro | 22 | ||||
| -rw-r--r-- | web/src/pages/index.astro | 45 | ||||
| -rw-r--r-- | web/src/styles/Quote.css | 33 | ||||
| -rw-r--r-- | web/tsconfig.json | 14 |
13 files changed, 300 insertions, 0 deletions
diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..016b59e --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,24 @@ +# build output +dist/ + +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store + +# jetbrains setting folder +.idea/ diff --git a/web/.vscode/extensions.json b/web/.vscode/extensions.json new file mode 100644 index 0000000..22a1505 --- /dev/null +++ b/web/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["astro-build.astro-vscode"], + "unwantedRecommendations": [] +} diff --git a/web/.vscode/launch.json b/web/.vscode/launch.json new file mode 100644 index 0000000..d642209 --- /dev/null +++ b/web/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/web/Dockerfile b/web/Dockerfile new file mode 100644 index 0000000..e021df4 --- /dev/null +++ b/web/Dockerfile @@ -0,0 +1,9 @@ +FROM oven/bun:latest + +WORKDIR /web + +COPY . . + +RUN bun run build + +ENTRYPOINT ["bun", "run", "/web/dist/server/entry.mjs"] diff --git a/web/astro.config.mjs b/web/astro.config.mjs new file mode 100644 index 0000000..d32e23b --- /dev/null +++ b/web/astro.config.mjs @@ -0,0 +1,21 @@ +// @ts-check +import { defineConfig, envField } from 'astro/config'; + +import react from '@astrojs/react'; + +import node from '@astrojs/node'; + +// https://astro.build/config +export default defineConfig({ + integrations: [react()], + + adapter: node({ + mode: 'standalone' + }), + + env: { + schema: { + API_URL: envField.string({ context: 'client', access: 'public' }), + } + } +}); diff --git a/web/bun.lockb b/web/bun.lockb Binary files differnew file mode 100644 index 0000000..26b85b4 --- /dev/null +++ b/web/bun.lockb diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..2a7be8a --- /dev/null +++ b/web/package.json @@ -0,0 +1,20 @@ +{ + "name": "aleebot-web", + "type": "module", + "version": "0.0.1", + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@astrojs/node": "^9.0.0", + "@astrojs/react": "^4.1.3", + "@types/react": "^19.0.4", + "@types/react-dom": "^19.0.2", + "astro": "^5.1.5", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } +} diff --git a/web/public/favicon.svg b/web/public/favicon.svg new file mode 100644 index 0000000..f157bd1 --- /dev/null +++ b/web/public/favicon.svg @@ -0,0 +1,9 @@ +<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128"> + <path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" /> + <style> + path { fill: #000; } + @media (prefers-color-scheme: dark) { + path { fill: #FFF; } + } + </style> +</svg> diff --git a/web/src/components/Quotes.jsx b/web/src/components/Quotes.jsx new file mode 100644 index 0000000..1eb258a --- /dev/null +++ b/web/src/components/Quotes.jsx @@ -0,0 +1,88 @@ +import { useState, useEffect } from 'react'; +import '../styles/Quote.css' +import { API_URL } from "astro:env/client"; + +export function PendingQuotes() { + const [quotes, setQuotes] = useState([]); + + const fetchQuotes = async () => { + try { + const response = await fetch(`${API_URL}/api/pending-quotes`); + const data = await response.json(); + setQuotes(data); + } catch (error) { + console.error('Failed to fetch quotes:', error); + } + }; + + useEffect(() => { + fetchQuotes(); + }, []); + + const approveQuote = async (id) => { + try { + const response = await fetch(`${API_URL}/api/approve-quote`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ id }), + }); + + if (response.ok) { + fetchQuotes(); // Refresh the listing after approving the quote + } else { + console.error('Failed to approve quote'); + } + } catch (error) { + console.error('Error approving quote:', error); + } + }; + + const rejectQuote = async (id) => { + try { + const response = await fetch(`${API_URL}/api/reject-quote`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ id }), + }); + + if (response.ok) { + fetchQuotes(); // Refresh the listing after approving the quote + } else { + console.error('Failed to reject quote'); + } + } catch (error) { + console.error('Error rejecting quote:', error); + } + }; + + return ( + <div> + <h1>Pending Quotes</h1> + {quotes.length > 0 ? ( + <ul className="quoteList"> + {quotes.map((quote) => ( + <li key={quote.id} className="quoteList"> + <div className="quote"> + <div className="author"> + <img src={quote.authorImage} alt="No Profile" width="50" height="50"/> + <h1 className="quoteAuthor">{quote.author}</h1> + </div> + <p className="quoteText">{quote.quote}</p> + <small>- {quote.year}</small> + <small>Submitted by {quote.submitterAuthor} ({quote.submitterID})</small> + </div> + <button onClick={() => approveQuote(quote.id)}>Approve</button> + <button onClick={() => rejectQuote(quote.id)}>Reject</button> + </li> + ))} + </ul> + ) : ( + <p>No pending quotes available.</p> + )} + </div> + ); +} diff --git a/web/src/layouts/Layout.astro b/web/src/layouts/Layout.astro new file mode 100644 index 0000000..2f6032d --- /dev/null +++ b/web/src/layouts/Layout.astro @@ -0,0 +1,22 @@ +<!doctype html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width" /> + <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> + <meta name="generator" content={Astro.generator} /> + <title>AleeBot Web Interface</title> + </head> + <body> + <slot /> + </body> +</html> + +<style> + html, + body { + margin: 0; + width: 100%; + height: 100%; + } +</style> diff --git a/web/src/pages/index.astro b/web/src/pages/index.astro new file mode 100644 index 0000000..f1dc6e7 --- /dev/null +++ b/web/src/pages/index.astro @@ -0,0 +1,45 @@ +--- +import Layout from '../layouts/Layout.astro'; +import { PendingQuotes } from '../components/Quotes'; + +--- + +<Layout> + <div class="container"> + <h1 id="version">AleeBot</h1> + <PendingQuotes client:load /> + </div> +</Layout> + +<style> + @import url('https://fonts.googleapis.com/css2?family=Exo+2:ital,wght@0,100..900;1,100..900&display=swap'); + html, + body { + margin: 0; + width: 100%; + height: 100%; + font-family: "Exo 2", sans-serif; + } + + .container { + margin: 2em; + } + +</style> + +<script> + import { API_URL } from "astro:env/client" + document.addEventListener('DOMContentLoaded', async () => { + try { + const version = await fetch(`${API_URL}/api/version`).then((res) => res.json()); + const versionElement = document.getElementById('version'); + if (versionElement) { + versionElement.textContent = `AleeBot ${version}`; + } else { + console.error('Element with ID "version" not found.'); + } + } catch (e) { + console.error('Failed to fetch version:', e); + } + }); +</script> diff --git a/web/src/styles/Quote.css b/web/src/styles/Quote.css new file mode 100644 index 0000000..8adfb29 --- /dev/null +++ b/web/src/styles/Quote.css @@ -0,0 +1,33 @@ +.quote { + display: flex; + flex-direction: column; + background: #555555; + color: #FFFFFF; + padding: 1em; +} + +ul.quoteList { + margin: 0; + padding: 0; +} + +li.quoteList { + list-style-type: none; + margin-top: 1em; + margin-bottom: 1em; +} + +.author { + display: flex; + flex-direction: row; +} + +h1.quoteAuthor { + font-size: 1.5em; + padding: 0; + margin: 0 0 0 .5em; +} + +.quoteText { + margin: .5em 0; +} diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 0000000..69c1600 --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [ + ".astro/types.d.ts", + "**/*" + ], + "exclude": [ + "dist" + ], + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "react" + } +}
\ No newline at end of file |
