Enhance backend and frontend setup with MySQL integration, Docker configurations, and toast notifications

- Updated .gitignore to include additional environment and build files
- Configured Dockerfiles for backend and frontend with npm install and port exposure
- Added MySQL connection pool and query function in backend services
- Implemented form submission with toast notifications in MainForm component
- Updated package.json and package-lock.json for new dependencies
- Enhanced routing and layout in frontend with toast notifications
This commit is contained in:
2025-08-11 19:56:43 +02:00
parent 08c820ac35
commit 0a2f1e650d
17 changed files with 411 additions and 15 deletions

View File

@@ -0,0 +1,12 @@
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 8001
CMD ["npm", "run", "dev"]

View File

@@ -13,6 +13,7 @@
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router-dom": "^7.8.0",
"react-toastify": "^11.0.5",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.11",
"tailwindcss-animate": "^1.0.7",
@@ -2371,6 +2372,15 @@
"node": ">=18"
}
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -3851,6 +3861,19 @@
"react-dom": ">=18"
}
},
"node_modules/react-toastify": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
"integrity": "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==",
"license": "MIT",
"dependencies": {
"clsx": "^2.1.1"
},
"peerDependencies": {
"react": "^18 || ^19",
"react-dom": "^18 || ^19"
}
},
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",

View File

@@ -15,6 +15,7 @@
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router-dom": "^7.8.0",
"react-toastify": "^11.0.5",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.11",
"tailwindcss-animate": "^1.0.7",

View File

@@ -1,6 +1,7 @@
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Admin from './components/Admin'; // Beispiel-Komponente
import Home from './components/Home'; // Beispiel-Komponente
import Admin from './components/Admin';
import Home from './components/Home';
import "react-toastify/dist/ReactToastify.css";
function App() {
return (

View File

@@ -1,6 +1,7 @@
import "../App.css";
import Layout from "../layout/Layout";
import MainForm from "./MainForm";
import "react-toastify/dist/ReactToastify.css";
function App() {
return (

View File

@@ -1,11 +1,16 @@
import React from "react";
import { registerLos } from "../utils/register";
import { toast } from "react-toastify";
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const data = Object.fromEntries(formData.entries());
await registerLos(data);
toast.promise(registerLos(data), {
pending: "Registriere Los...",
success: "Los erfolgreich registriert!",
error: "Fehler bei der Registrierung des Loses.",
});
};
const MainForm: React.FC = () => {
@@ -72,15 +77,15 @@ const MainForm: React.FC = () => {
<div className="space-y-1">
<label
htmlFor="strasse"
htmlFor="adresse"
className="text-sm font-medium text-zinc-800"
>
Straße + Haus Nr.:
</label>
<input
type="text"
id="strasse"
name="strasse"
id="adresse"
name="adresse"
placeholder="Musterstraße 1"
required
className="w-full rounded-xl border border-black/25 bg-white/80 px-4 py-2.5 text-sm text-zinc-800 placeholder-zinc-400 shadow-inner outline-none focus:border-black/40 focus:ring-2 focus:ring-black/10"

View File

@@ -1,6 +1,7 @@
import React from "react";
import Header from "../components/Header";
import "../App.css";
import { ToastContainer } from "react-toastify";
type LayoutProps = {
children: React.ReactNode;
@@ -12,6 +13,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
<Header />
<main>{children}</main>
<footer></footer>
<ToastContainer />
</>
);
};

View File

@@ -1,3 +1,17 @@
import { myToast } from "./toastify";
export const registerLos = async (data: any) => {
console.log(data);
}
await fetch("http://localhost:8002/lose", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
}).then((response) => {
if (response.ok) {
myToast("Los erfolgreich registriert!", "success");
} else {
myToast("Fehler bei der Registrierung des Loses.", "error");
}
});
};

View File

@@ -0,0 +1,17 @@
import { toast, type ToastOptions } from "react-toastify";
export type ToastType = "success" | "error" | "info" | "warning";
export const myToast = (message: string, msgType: ToastType) => {
let config: ToastOptions = {
position: "top-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "dark",
};
toast[msgType](message, config);
};

View File

@@ -7,7 +7,7 @@ export default defineConfig({
plugins: [react(), svgr(), tailwindcss()],
server: {
host: "0.0.0.0",
port: 5001,
port: 8001,
watch: {
usePolling: true,
},