Compare commits

21 Commits

Author SHA1 Message Date
3bd5cc138c Split the file.
All checks were successful
Build and Push Docker Image to Gitea Registry / build-and-push (push) Successful in 1m9s
2025-01-26 00:45:28 +01:00
5733f19673 Split the file.
Some checks failed
Build and Push Docker Image to Gitea Registry / build-and-push (push) Failing after 7s
2025-01-26 00:43:34 +01:00
bda954787f Neue Nachricht Button.
All checks were successful
Build and Push Docker Image to Gitea Registry / build-and-push (push) Successful in 13s
2025-01-26 00:34:27 +01:00
e05b9dfaf1 nginx update
All checks were successful
Build and Push Docker Image to Gitea Registry / build-and-push (push) Successful in 17s
2025-01-26 00:25:40 +01:00
ed795097a8 nginx update
Some checks failed
Build and Push Docker Image to Gitea Registry / build-and-push (push) Failing after 5s
2025-01-26 00:23:38 +01:00
6a3b5f780b react test
All checks were successful
Build and Push Docker Image to Gitea Registry / build-and-push (push) Successful in 55s
2025-01-26 00:11:33 +01:00
fbf79c71ac react test
Some checks failed
Build and Push Docker Image to Gitea Registry / build-and-push (push) Failing after 26s
2025-01-26 00:08:35 +01:00
fadca54b3c react test 2025-01-26 00:08:11 +01:00
79c985bada Message board: Fix message char counter.
All checks were successful
Build and Push Docker Image to Gitea Registry / build-and-push (push) Successful in 5s
2025-01-25 23:38:26 +01:00
49ba14c001 Message board: Fix message char counter.
All checks were successful
Build and Push Docker Image to Gitea Registry / build-and-push (push) Successful in 4s
2025-01-25 23:35:33 +01:00
b039a86bb2 Message board: Fix the char counter reset on submit.
All checks were successful
Build and Push Docker Image to Gitea Registry / build-and-push (push) Successful in 11s
2025-01-25 23:30:06 +01:00
915d8493f4 Merge pull request 'char counter in mesage box' (#8) from message_board into main
All checks were successful
Build and Push Docker Image to Gitea Registry / build-and-push (push) Successful in 4s
Reviewed-on: #8
2025-01-17 01:35:24 +00:00
f4e06bc29a char counter in mesage box 2025-01-17 02:35:03 +01:00
1efe5792d2 Merge pull request 'char counter in mesage box' (#7) from message_board into main
All checks were successful
Build and Push Docker Image to Gitea Registry / build-and-push (push) Successful in 5s
Reviewed-on: #7
2025-01-17 01:32:56 +00:00
a47d56b8c8 char counter in mesage box 2025-01-17 02:32:20 +01:00
1647e6ab1b Merge pull request 'message_board' (#6) from message_board into main
All checks were successful
Build and Push Docker Image to Gitea Registry / build-and-push (push) Successful in 5s
Reviewed-on: #6
2025-01-17 01:21:54 +00:00
jafreli
36787704d7 Merge branch 'message_board' of https://git.out.jafre.li/jafreli/Linktree into message_board 2025-01-17 02:19:24 +01:00
jafreli
5f51e4930d changed style 2025-01-17 02:18:58 +01:00
f556fda1c8 Merge pull request 'changed style' (#5) from message_board into main
All checks were successful
Build and Push Docker Image to Gitea Registry / build-and-push (push) Successful in 5s
Reviewed-on: #5
2025-01-17 01:14:28 +00:00
4c3acfa2d0 Merge branch 'main' into message_board 2025-01-17 01:13:46 +00:00
jafreli
231c2160d6 changed style 2025-01-17 02:13:10 +01:00
14 changed files with 268 additions and 14 deletions

View File

@@ -4,7 +4,7 @@ on:
push: push:
branches: branches:
- main - main
- massage_board - jbin
jobs: jobs:
build-and-push: build-and-push:

View File

@@ -1,11 +1,32 @@
# Dockerfile # Stage 1: Build React App
FROM node:18 as build
# Set working directory
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the source code
COPY . .
# Build the app
RUN npm run build
# Stage 2: Serve the app using Nginx
FROM nginx:alpine FROM nginx:alpine
# Kopiere das gesamte 'view' Verzeichnis in das Nginx-HTML-Verzeichnis # Copy custom Nginx configuration
COPY view/ /usr/share/nginx/html/ COPY nginx.conf /etc/nginx/conf.d/default.conf
# Exponiere den Port 80 für den Container # Copy build artifacts to Nginx's web root
COPY --from=build /app/build /usr/share/nginx/html
# Expose port 80
EXPOSE 80 EXPOSE 80
# Standardbefehl zum Starten von Nginx # Start Nginx
CMD ["nginx", "-g", "daemon off;"] CMD ["nginx", "-g", "daemon off;"]

13
nginx.conf Normal file
View File

@@ -0,0 +1,13 @@
server {
listen 80;
server_name test.out.jafre.li;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri /index.html;
}
error_page 404 /index.html;
}

36
package.json Normal file
View File

@@ -0,0 +1,36 @@
{
"name": "nachrichten-app",
"version": "1.0.0",
"private": true,
"dependencies": {
"pocketbase": "^0.13.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "^5.0.1",
"react-router-dom": "^6.14.1"
},
"devDependencies": {
"tailwindcss": "^3.3.2",
"autoprefixer": "^10.4.14",
"postcss": "^8.4.24"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

17
public/index.html Normal file
View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Nachrichten App</title>
<!-- Favicon -->
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- Tailwind CSS (optional if used) -->
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100">
<!-- Root element where React mounts -->
<div id="root"></div>
</body>
</html>

17
src/App.jsx Normal file
View File

@@ -0,0 +1,17 @@
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Home from './components/Home';
import Message from './components/Message';
const App = () => {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path=":id" element={<Message />} />
</Routes>
</Router>
);
};
export default App;

36
src/components/Home.jsx Normal file
View File

@@ -0,0 +1,36 @@
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import PocketBase from 'pocketbase';
import MessageForm from './MessageForm';
const pb = new PocketBase('https://db.out.jafre.li');
const Home = () => {
const [submitted, setSubmitted] = useState(false);
const [error, setError] = useState(null);
const handleSubmit = async (message) => {
try {
const data = { message };
const record = await pb.collection('jbin').create(data);
setSubmitted(record.id);
} catch (err) {
setError('Fehler beim Erstellen der Nachricht');
}
};
return (
<div className="flex flex-col items-center p-4">
<h1 className="text-2xl font-bold mb-4">Nachricht erstellen</h1>
<MessageForm onSubmit={handleSubmit} />
{submitted && (
<p className="mt-4 text-green-500">
Nachricht erstellt! Schaue sie dir hier an: <Link to={`/${submitted}`} className="text-blue-500 underline">/{submitted}</Link>
</p>
)}
{error && <p className="mt-4 text-red-500">{error}</p>}
</div>
);
};
export default Home;

View File

@@ -0,0 +1,46 @@
import React, { useState, useEffect } from 'react';
import PocketBase from 'pocketbase';
import { useParams, Link } from 'react-router-dom';
const pb = new PocketBase('https://db.out.jafre.li');
const Message = () => {
const { id } = useParams();
const [message, setMessage] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
const fetchMessage = async () => {
try {
const record = await pb.collection('jbin').getOne(id);
setMessage(record.message);
} catch (err) {
setError('Nachricht nicht gefunden');
}
};
fetchMessage();
}, [id]);
return (
<div className="flex flex-col items-center p-4">
{error ? (
<h1 className="text-xl font-bold text-red-500">{error}</h1>
) : message ? (
<>
<h1 className="text-2xl font-bold mb-4">Nachricht</h1>
<p className="p-4 border rounded-xl bg-gray-100 w-full max-w-md">{message}</p>
</>
) : (
<p className="text-gray-500">Lädt...</p>
)}
<Link to="/" className="mt-4">
<button className="bg-blue-500 text-white py-2 px-4 rounded-xl hover:bg-blue-600">
Neue Nachricht
</button>
</Link>
</div>
);
};
export default Message;

View File

@@ -0,0 +1,28 @@
import React, { useState } from 'react';
const MessageForm = ({ onSubmit }) => {
const [message, setMessage] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(message);
setMessage('');
};
return (
<form onSubmit={handleSubmit} className="flex flex-col gap-4 w-full max-w-md">
<textarea
className="p-2 border rounded-xl w-full"
placeholder="Nachricht hier eingeben..."
value={message}
onChange={(e) => setMessage(e.target.value)}
required
></textarea>
<button type="submit" className="bg-blue-500 text-white py-2 rounded-xl hover:bg-blue-600">
Nachricht posten
</button>
</form>
);
};
export default MessageForm;

11
src/index.js Normal file
View File

@@ -0,0 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
//import './index.css'; // Falls du globale Styles hast
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);

View File

@@ -5,19 +5,23 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Message Board</title> <title>Message Board</title>
<link rel="stylesheet" href="styles.css"> <link rel="stylesheet" href="styles.css">
<link rel="apple-touch-icon" sizes="180x180" href="logos/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="logos/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="logos/favicon/favicon-16x16.png">
<script src="https://cdn.jsdelivr.net/npm/pocketbase/dist/pocketbase.umd.js"></script> <script src="https://cdn.jsdelivr.net/npm/pocketbase/dist/pocketbase.umd.js"></script>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h1>Message Board</h1> <h1>Message Board</h1>
<form id="messageForm">
<input type="text" id="name" placeholder="Your Name" required>
<textarea id="message" placeholder="Your Message" maxlength="100" required></textarea>
<div id="charCount">0 / 100</div>
<button type="submit">Send</button>
</form>
<div id="messages"> <div id="messages">
<!-- Nachrichten werden hier dynamisch geladen --> <!-- Nachrichten werden hier dynamisch geladen -->
</div> </div>
<form id="messageForm">
<input type="text" id="name" placeholder="Your Name" required>
<textarea id="message" placeholder="Your Message" required></textarea>
<button type="submit">Send</button>
</form>
</div> </div>
<script src="message-board.js"></script> <script src="message-board.js"></script>
</body> </body>

View File

@@ -1,10 +1,13 @@
const pb = new PocketBase('https://db.out.jafre.li'); const pb = new PocketBase('https://db.out.jafre.li');
const messageContainer = document.getElementById('messages'); const messageContainer = document.getElementById('messages');
const messageForm = document.getElementById('messageForm'); const messageForm = document.getElementById('messageForm');
const charCount = document.getElementById('charCount');
const messageInput = document.getElementById('message');
// Funktion, um die letzten 10 Nachrichten zu laden // Funktion, um die letzten 10 Nachrichten zu laden
async function loadMessages() { async function loadMessages() {
const result = await pb.collection('message_board').getList(1, 10, { const result = await pb.collection('message_board').getList(1, 8, {
sort: '-created', // Sortiert nach Erstellungsdatum absteigend sort: '-created', // Sortiert nach Erstellungsdatum absteigend
}); });
@@ -33,6 +36,7 @@ messageForm.addEventListener('submit', async (e) => {
await pb.collection('message_board').create({ name, message }); await pb.collection('message_board').create({ name, message });
loadMessages(); // Aktualisiert die Nachrichtenanzeige loadMessages(); // Aktualisiert die Nachrichtenanzeige
messageForm.reset(); // Löscht das Formular messageForm.reset(); // Löscht das Formular
charCount.textContent = `0 / 100`;
} catch (error) { } catch (error) {
console.error('Error creating message:', error); console.error('Error creating message:', error);
} }
@@ -40,3 +44,9 @@ messageForm.addEventListener('submit', async (e) => {
// Nachrichten beim Laden der Seite anzeigen // Nachrichten beim Laden der Seite anzeigen
loadMessages(); loadMessages();
// Event-Listener für Eingaben im Textfeld
messageInput.addEventListener('input', () => {
const currentLength = messageInput.value.length;
charCount.textContent = `${currentLength} / 100`;
});

View File

@@ -19,7 +19,7 @@ body {
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; /* Zentriert die Links im Container */ align-items: center; /* Zentriert die Inhalte im Container */
} }
h1 { h1 {
@@ -76,17 +76,26 @@ h1 {
form { form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; /* Zentriert die Form-Inhalte horizontal */
gap: 10px; gap: 10px;
width: 100%; /* Maximale Breite der Form */
} }
form input, form textarea { form input, form textarea {
width: 100%; width: 90%; /* Eingabefelder nehmen 90% der Breite ein */
padding: 10px; padding: 10px;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 5px; border-radius: 5px;
box-sizing: border-box; /* Verhindert, dass Padding die Größe beeinflusst */
}
form textarea {
height: 100px; /* Größeres Textfeld */
resize: none; /* Deaktiviert Größenänderungen durch den Benutzer */
} }
form button { form button {
width: 50%; /* Sende-Knopf nimmt 50% der Form-Breite ein */
padding: 10px; padding: 10px;
background: #007BFF; background: #007BFF;
color: white; color: white;
@@ -100,3 +109,9 @@ form button:hover {
background: #0056b3; background: #0056b3;
} }
#charCount {
font-size: 12px;
color: #666;
text-align: right; /* Rechtsbündig unter dem Textfeld */
width: 90%;
}