This commit is contained in:
Michaela Grabke 2024-09-09 15:21:41 +02:00
parent bbc0478d23
commit 39d6b6f0bb
8 changed files with 395 additions and 20 deletions

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

172
package-lock.json generated
View File

@ -11,10 +11,17 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.7.7",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.19.2",
"formik": "^2.4.6",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
"tailwindcss": "^3.4.10",
"web-vitals": "^2.1.4",
"yup": "^1.4.0"
}
},
"node_modules/@adobe/css-tools": {
@ -4358,6 +4365,16 @@
"@types/node": "*"
}
},
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz",
"integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==",
"license": "MIT",
"dependencies": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"node_modules/@types/html-minifier-terser": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
@ -5811,6 +5828,31 @@
"node": ">=4"
}
},
"node_modules/axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axios/node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@ -6826,6 +6868,19 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"license": "MIT"
},
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"license": "MIT",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/cosmiconfig": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
@ -9415,6 +9470,40 @@
"node": ">= 6"
}
},
"node_modules/formik": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz",
"integrity": "sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g==",
"funding": [
{
"type": "individual",
"url": "https://opencollective.com/formik"
}
],
"license": "Apache-2.0",
"dependencies": {
"@types/hoist-non-react-statics": "^3.3.1",
"deepmerge": "^2.1.1",
"hoist-non-react-statics": "^3.3.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"react-fast-compare": "^2.0.1",
"tiny-warning": "^1.0.2",
"tslib": "^2.0.0"
},
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/formik/node_modules/deepmerge": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@ -9866,6 +9955,21 @@
"he": "bin/he"
}
},
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"license": "BSD-3-Clause",
"dependencies": {
"react-is": "^16.7.0"
}
},
"node_modules/hoist-non-react-statics/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
"node_modules/hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@ -13400,6 +13504,12 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"license": "MIT"
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@ -15804,6 +15914,12 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
"node_modules/property-expr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
"integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==",
"license": "MIT"
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@ -15826,6 +15942,12 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@ -16197,6 +16319,12 @@
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==",
"license": "MIT"
},
"node_modules/react-fast-compare": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==",
"license": "MIT"
},
"node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@ -18237,6 +18365,18 @@
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
"license": "MIT"
},
"node_modules/tiny-case": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
"integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==",
"license": "MIT"
},
"node_modules/tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"license": "MIT"
},
"node_modules/tmpl": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@ -18273,6 +18413,12 @@
"node": ">=0.6"
}
},
"node_modules/toposort": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==",
"license": "MIT"
},
"node_modules/tough-cookie": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
@ -19773,6 +19919,30 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/yup": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/yup/-/yup-1.4.0.tgz",
"integrity": "sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==",
"license": "MIT",
"dependencies": {
"property-expr": "^2.0.5",
"tiny-case": "^1.0.3",
"toposort": "^2.0.2",
"type-fest": "^2.19.0"
}
},
"node_modules/yup/node_modules/type-fest": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
"integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=12.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
}
}
}

View File

@ -6,10 +6,17 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.7.7",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.19.2",
"formik": "^2.4.6",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
"tailwindcss": "^3.4.10",
"web-vitals": "^2.1.4",
"yup": "^1.4.0"
},
"scripts": {
"start": "react-scripts start",

View File

@ -1,24 +1,14 @@
import logo from './logo.svg';
import './App.css';
import React from "react";
import SpendenForm from "./components/SpendenForm";
import './index.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
<div className="App">
<main className="p-4">
<SpendenForm />
</main>
</div>
);
}

View File

@ -0,0 +1,162 @@
import React from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import axios from "axios";
// Manuelle Validierung
const validateForm = (values) => {
const errors = {};
if (!values.kleidungsart) {
errors.kleidungsart = "Bitte wählen Sie eine Kleidungsart";
}
if (!values.krisengebiet) {
errors.krisengebiet = "Bitte wählen Sie ein Krisengebiet";
}
if (!values.lieferart) {
errors.lieferart = "Bitte wählen Sie eine Lieferart";
}
if (values.lieferart === "Abholung") {
if (!values.abholadresse) {
errors.abholadresse = "Bitte geben Sie Ihre Abholadresse an";
}
if (!values.plz) {
errors.plz = "Bitte geben Sie eine PLZ an";
} else if (!/^\d{5}$/.test(values.plz)) {
errors.plz = "Die PLZ muss genau 5 Ziffern haben";
}
}
return errors;
};
const SpendenForm = () => {
const handleSubmit = (values) => {
axios
.post("http://localhost:5000/api/spenden", values)
.then((response) => {
alert("Spende erfolgreich registriert!");
})
.catch((error) => {
console.error("Es gab ein Problem bei der Registrierung:", error);
});
};
return (
<div className="flex flex-col min-h-screen">
<header className="bg-purple-600 text-white p-4 text-center">
<h1 className="text-3xl font-bold">Kleiderspende Registrierung</h1>
</header>
<main className="flex-grow p-4 bg-gray-100">
<Formik
initialValues={{
kleidungsart: "",
krisengebiet: "",
lieferart: "Übergabe",
abholadresse: "",
plz: "",
}}
validate={validateForm}
onSubmit={handleSubmit}
>
{({ values }) => (
<Form className="max-w-lg mx-auto p-6 bg-white shadow-lg rounded-lg">
<div className="mb-6">
<label htmlFor="kleidungsart" className="block text-gray-700 font-semibold mb-2">
Kleidungsart
</label>
<Field
as="select"
name="kleidungsart"
className="border rounded-md w-full p-3 focus:outline-none focus:ring-2 focus:ring-purple-500"
>
<option value="">Wählen Sie...</option>
<option value="jacke">Jacke</option>
<option value="hose">Hose</option>
<option value="tshirt">T-Shirt</option>
</Field>
<ErrorMessage name="kleidungsart" component="div" className="text-red-600 mt-1" />
</div>
<div className="mb-6">
<label htmlFor="krisengebiet" className="block text-gray-700 font-semibold mb-2">
Krisengebiet
</label>
<Field
as="select"
name="krisengebiet"
className="border rounded-md w-full p-3 focus:outline-none focus:ring-2 focus:ring-purple-500"
>
<option value="">Wählen Sie...</option>
<option value="ukraine">Ukraine</option>
<option value="syrien">Syrien</option>
<option value="erdbeben">Erdbebenregion</option>
</Field>
<ErrorMessage name="krisengebiet" component="div" className="text-red-600 mt-1" />
</div>
<div className="mb-6">
<label className="block text-gray-700 font-semibold mb-2">Lieferart</label>
<div className="space-x-4">
<label>
<Field type="radio" name="lieferart" value="Übergabe" className="mr-2" />
Übergabe an Geschäftsstelle
</label>
<label>
<Field type="radio" name="lieferart" value="Abholung" className="mr-2" />
Abholung
</label>
</div>
</div>
{values.lieferart === "Abholung" && (
<>
<div className="mb-6">
<label htmlFor="abholadresse" className="block text-gray-700 font-semibold mb-2">
Abholadresse
</label>
<Field
type="text"
name="abholadresse"
className="border rounded-md w-full p-3 focus:outline-none focus:ring-2 focus:ring-purple-500"
/>
<ErrorMessage name="abholadresse" component="div" className="text-red-600 mt-1" />
</div>
<div className="mb-6">
<label htmlFor="plz" className="block text-gray-700 font-semibold mb-2">
Postleitzahl
</label>
<Field
type="text"
name="plz"
className="border rounded-md w-full p-3 focus:outline-none focus:ring-2 focus:ring-purple-500"
/>
<ErrorMessage name="plz" component="div" className="text-red-600 mt-1" />
</div>
</>
)}
<button
type="submit"
className="bg-purple-300 text-white w-full py-3 rounded-md font-semibold hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-500"
>
Spende registrieren
</button>
</Form>
)}
</Formik>
</main>
<footer className="bg-gray-800 text-white text-center p-4">
<p>&copy; 2024 Lokaler Verein. Alle Rechte vorbehalten.</p>
</footer>
</div>
);
};
export default SpendenForm;

View File

@ -1,3 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',

30
src/start.js Normal file
View File

@ -0,0 +1,30 @@
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const app = express();
app.use(cors());
app.use(bodyParser.json());
const PLZ_GESCHAEFTSSTELLE = "10115"; // Beispiel-PLZ
app.post('/api/spenden', (req, res) => {
const { kleidungsart, krisengebiet, lieferart, abholadresse, plz } = req.body;
// PLZ-Validierung
if (lieferart === "Abholung" && plz.substring(0, 2) !== PLZ_GESCHAEFTSSTELLE.substring(0, 2)) {
return res.status(400).json({ error: "Die Abholadresse liegt nicht im gleichen PLZ-Gebiet." });
}
// Spende registrieren (Hier könnte man Daten in einer DB speichern)
console.log("Spende registriert:", req.body);
res.status(200).json({
message: "Spende erfolgreich registriert!",
data: req.body,
});
});
app.listen(2000, () => {
console.log('Server läuft auf Port 2000');
});