Init
This commit is contained in:
commit
543aca02ba
|
@ -0,0 +1,4 @@
|
|||
node_modules
|
||||
dist
|
||||
# Keep environment variables out of version control
|
||||
.env
|
|
@ -0,0 +1,841 @@
|
|||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@prisma/client": "^5.3.1",
|
||||
"chalk": "^4.0.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"prisma": "^5.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "latest",
|
||||
"@types/node": "latest",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.3.1.tgz",
|
||||
"integrity": "sha512-ArOKjHwdFZIe1cGU56oIfy7wRuTn0FfZjGuU/AjgEBOQh+4rDkB6nF+AGHP8KaVpkBIiHGPQh3IpwQ3xDMdO0Q==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines-version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.13"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prisma": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"prisma": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.3.1.tgz",
|
||||
"integrity": "sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA==",
|
||||
"hasInstallScript": true
|
||||
},
|
||||
"node_modules/@prisma/engines-version": {
|
||||
"version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59.tgz",
|
||||
"integrity": "sha512-y5qbUi3ql2Xg7XraqcXEdMHh0MocBfnBzDn5GbV1xk23S3Mq8MGs+VjacTNiBh3dtEdUERCrUUG7Z3QaJ+h79w=="
|
||||
},
|
||||
"node_modules/@types/body-parser": {
|
||||
"version": "1.19.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz",
|
||||
"integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/connect": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/connect": {
|
||||
"version": "3.4.36",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz",
|
||||
"integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "4.17.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
|
||||
"integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^4.17.33",
|
||||
"@types/qs": "*",
|
||||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "4.17.36",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.36.tgz",
|
||||
"integrity": "sha512-zbivROJ0ZqLAtMzgzIUC4oNqDG9iF0lSsAqpOD9kbs5xcIM3dTiyuHvBc7R8MtWBp3AAWGaovJa+wzWPjLYW7Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@types/qs": "*",
|
||||
"@types/range-parser": "*",
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/http-errors": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz",
|
||||
"integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz",
|
||||
"integrity": "sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.9.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz",
|
||||
"integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/range-parser": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
|
||||
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/send": {
|
||||
"version": "0.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz",
|
||||
"integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/mime": "^1",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz",
|
||||
"integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/http-errors": "*",
|
||||
"@types/mime": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.4",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"raw-body": "2.5.1",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz",
|
||||
"integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
||||
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/motdotla/dotenv?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||
},
|
||||
"node_modules/etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.18.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.1",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.5.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "1.2.0",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"merge-descriptors": "1.0.1",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.11.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.18.0",
|
||||
"serve-static": "1.15.0",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
||||
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"statuses": "2.0.1",
|
||||
"unpipe": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-proto": "^1.0.1",
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/has-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||
"dependencies": {
|
||||
"depd": "2.0.0",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"toidentifier": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.3.1.tgz",
|
||||
"integrity": "sha512-Wp2msQIlMPHe+5k5Od6xnsI/WNG7UJGgFUJgqv/ygc7kOECZapcSz/iU4NIEzISs3H1W9sFLjAPbg/gOqqtB7A==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines": "5.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"prisma": "build/index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.13"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"dependencies": {
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
|
||||
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"mime": "1.6.0",
|
||||
"ms": "2.1.3",
|
||||
"on-finished": "2.4.1",
|
||||
"range-parser": "~1.2.1",
|
||||
"statuses": "2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
|
||||
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
|
||||
"dependencies": {
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.18.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.0",
|
||||
"get-intrinsic": "^1.0.2",
|
||||
"object-inspect": "^1.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"dependencies": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
||||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "./dist/index.js",
|
||||
"scripts": {
|
||||
"start": "npx tsc && node dist/index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@prisma/client": "^5.3.1",
|
||||
"chalk": "^4.0.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"prisma": "^5.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "latest",
|
||||
"@types/node": "latest",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
previewFeatures = ["postgresqlExtensions"]
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
extensions = [uuid_ossp(map: "uuid-ossp")]
|
||||
}
|
||||
|
||||
model articles {
|
||||
ID String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
|
||||
authorID String @db.Uuid
|
||||
title String @db.VarChar(100)
|
||||
views Int @default(0)
|
||||
public Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
author users @relation(fields: [authorID], references: [ID])
|
||||
sponsors sponsors[]
|
||||
}
|
||||
|
||||
model users {
|
||||
ID String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
|
||||
username String @db.VarChar(64)
|
||||
password String @db.VarChar(64)
|
||||
token String @db.VarChar(128)
|
||||
admin Boolean @default(false)
|
||||
article_create Boolean @default(false)
|
||||
article_manage Boolean @default(false)
|
||||
sponsor_manage Boolean @default(false)
|
||||
user_manage Boolean @default(false)
|
||||
|
||||
articles articles[]
|
||||
|
||||
@@unique([username])
|
||||
@@unique([token])
|
||||
}
|
||||
|
||||
model sponsors {
|
||||
ID String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
|
||||
name String @db.VarChar(12)
|
||||
url String @db.VarChar(128)
|
||||
description String @db.VarChar(100)
|
||||
|
||||
addedAt DateTime @default(now())
|
||||
|
||||
articles articles[]
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import { prisma } from "."
|
||||
|
||||
export function errorBuilder(type: string, message: string): Types.ErrorResponse {
|
||||
return {
|
||||
error: true,
|
||||
type,
|
||||
message
|
||||
}
|
||||
}
|
||||
|
||||
const Errors = {
|
||||
MISSING_ITEMS: errorBuilder("missing_items", "Die Anfrage ist Fehlerhaft."),
|
||||
INVALID_CREDENTIALS: errorBuilder("invalid_credentials", "Die eingegebenen Anmeldedaten sind inkorrekt!"),
|
||||
NOT_FOUND: errorBuilder("not_found", "Die angeforderte Ressource wurde nicht gefunden!"),
|
||||
}
|
||||
export default Errors
|
||||
|
||||
export async function authorize(token: string) {
|
||||
const user = await prisma.users.findFirst({
|
||||
where: {
|
||||
token
|
||||
}
|
||||
})
|
||||
|
||||
if (!user) return false
|
||||
return user
|
||||
}
|
|
@ -0,0 +1,391 @@
|
|||
import { PrismaClient } from '@prisma/client'
|
||||
import express from 'express'
|
||||
import dotenv from 'dotenv'
|
||||
import init from './init'
|
||||
import Errors, { authorize } from './functions'
|
||||
import fs from 'fs'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
const app = express()
|
||||
|
||||
fs.mkdirSync("/var/lib/rheinefuerrheine/artikel", { recursive: true })
|
||||
fs.mkdirSync("/var/lib/rheinefuerrheine/sponsoren", { recursive: true })
|
||||
|
||||
app.use(express.json({
|
||||
limit: '50mb'
|
||||
}))
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader("Access-Control-Allow-Methods", "*");
|
||||
res.setHeader("Access-Control-Allow-Headers", 'Content-Type, Authorization');
|
||||
|
||||
next();
|
||||
})
|
||||
|
||||
export { prisma, app }
|
||||
init()
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.send("You took the wrong turn, buddy.")
|
||||
})
|
||||
|
||||
app.post('/api/auth/login', async (req, res) => {
|
||||
const { username, password } = req.body
|
||||
|
||||
if (!username || !password) {
|
||||
res.status(400).send(Errors.MISSING_ITEMS)
|
||||
return
|
||||
}
|
||||
|
||||
const user = await prisma.users.findFirst({
|
||||
where: {
|
||||
username
|
||||
}
|
||||
})
|
||||
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
if (user.password !== password) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
res.send({
|
||||
token: user.token
|
||||
})
|
||||
})
|
||||
|
||||
app.get('/api/auth/verify', async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
if (!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const user = await authorize(token)
|
||||
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
res.send('OK')
|
||||
})
|
||||
|
||||
app.post("/api/article/create", async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
if(!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const user = await authorize(token)
|
||||
if(!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const { title, content, image } = req.body
|
||||
if(!title || !content || !image) return res.status(400).send(Errors.MISSING_ITEMS)
|
||||
|
||||
const article = await prisma.articles.create({
|
||||
data: {
|
||||
title,
|
||||
author: {
|
||||
connect: {
|
||||
ID: user.ID
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
fs.writeFileSync(`/var/lib/rheinefuerrheine/artikel/${article.ID}.html`, content)
|
||||
fs.writeFileSync(`/var/lib/rheinefuerrheine/artikel/${article.ID}.banner`, image);
|
||||
|
||||
res.send(article)
|
||||
})
|
||||
|
||||
app.post("/api/article/edit/:id", async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
if(!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const user = await authorize(token)
|
||||
if(!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const { title, content, image } = req.body
|
||||
if(!title || !content || !image || !req.params.id) return res.status(400).send(Errors.MISSING_ITEMS)
|
||||
|
||||
const article = await prisma.articles.update({
|
||||
where: {
|
||||
ID: req.params.id
|
||||
},
|
||||
data: {
|
||||
title,
|
||||
}
|
||||
})
|
||||
|
||||
fs.writeFileSync(`/var/lib/rheinefuerrheine/artikel/${article.ID}.html`, content)
|
||||
fs.writeFileSync(`/var/lib/rheinefuerrheine/artikel/${article.ID}.banner`, image);
|
||||
|
||||
res.send(article)
|
||||
})
|
||||
|
||||
app.get('/api/article/view/:id', async (req, res) => {
|
||||
const article = await prisma.articles.update({
|
||||
where: {
|
||||
ID: req.params.id,
|
||||
},
|
||||
data: {
|
||||
views: {
|
||||
increment: 1
|
||||
}
|
||||
},
|
||||
select: {
|
||||
ID: true,
|
||||
title: true,
|
||||
views: true,
|
||||
author: {
|
||||
select: {
|
||||
ID: true,
|
||||
username: true
|
||||
}
|
||||
},
|
||||
sponsors: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
}
|
||||
})
|
||||
|
||||
res.send(article)
|
||||
})
|
||||
|
||||
app.get("/api/article/content/:id", async (req, res) => {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*')
|
||||
res.sendFile(`/var/lib/rheinefuerrheine/artikel/${req.params.id}.html`)
|
||||
})
|
||||
|
||||
app.get("/api/allArticles", async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
if(!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const user = await authorize(token)
|
||||
if(!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const articles = await prisma.articles.findMany({
|
||||
select: {
|
||||
ID: true,
|
||||
title: true,
|
||||
views: true,
|
||||
author: {
|
||||
select: {
|
||||
ID: true,
|
||||
username: true
|
||||
}
|
||||
},
|
||||
sponsors: true,
|
||||
updatedAt: true,
|
||||
createdAt: true,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
},
|
||||
...((!user.admin && !user.article_manage) && {
|
||||
where: {
|
||||
authorID: user.ID
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
res.send(articles)
|
||||
})
|
||||
|
||||
app.get('/api/articles/public', async (req, res) => {
|
||||
const articles = await prisma.articles.findMany({
|
||||
select: {
|
||||
ID: true,
|
||||
title: true,
|
||||
views: true,
|
||||
author: {
|
||||
select: {
|
||||
ID: true,
|
||||
username: true
|
||||
}
|
||||
},
|
||||
sponsors: true,
|
||||
updatedAt: true,
|
||||
createdAt: true,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
},
|
||||
where: {
|
||||
public: true
|
||||
}
|
||||
})
|
||||
|
||||
res.send(articles)
|
||||
})
|
||||
|
||||
app.delete('/api/article/:article', async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
if(!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const user = await authorize(token)
|
||||
if(!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const article = await prisma.articles.findUnique({
|
||||
where: {
|
||||
ID: req.params.article
|
||||
},
|
||||
select: {
|
||||
ID: true,
|
||||
title: true,
|
||||
views: true,
|
||||
|
||||
author: {
|
||||
select: {
|
||||
ID: true,
|
||||
username: true
|
||||
}
|
||||
},
|
||||
sponsors: true,
|
||||
updatedAt: true,
|
||||
createdAt: true,
|
||||
}
|
||||
})
|
||||
|
||||
if(!article) return res.status(404).send(Errors.NOT_FOUND)
|
||||
|
||||
if((!user.admin && !user.article_manage) && article?.author.ID !== user.ID) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
await prisma.articles.delete({
|
||||
where: {
|
||||
ID: req.params.article
|
||||
}
|
||||
})
|
||||
|
||||
fs.rmSync(`/var/lib/rheinefuerrheine/artikel/${req.params.article}.html`)
|
||||
if(fs.existsSync(`/var/lib/rheinefuerrheine/artikel/${req.params.article}.banner`)) fs.rmSync(`/var/lib/rheinefuerrheine/artikel/${req.params.article}.banner`)
|
||||
|
||||
res.send('OK')
|
||||
})
|
||||
|
||||
app.post('/api/article/banner/:article', async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
if(!token || !req.body) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const user = await authorize(token)
|
||||
if(!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
fs.writeFileSync(`/var/lib/rheinefuerrheine/artikel/${req.params.article}.banner`, req.body.data);
|
||||
|
||||
res.send('OK')
|
||||
})
|
||||
|
||||
app.get('/api/article/banner/:article', async (req, res) => {
|
||||
if(!fs.existsSync(`/var/lib/rheinefuerrheine/artikel/${req.params.article}.banner`)) return res.status(404).send(Errors.NOT_FOUND)
|
||||
|
||||
res.sendFile(`/var/lib/rheinefuerrheine/artikel/${req.params.article}.banner`)
|
||||
})
|
||||
|
||||
|
||||
app.get('/api/sponsors/getAll', async (req, res) => {
|
||||
const sponsors = await prisma.sponsors.findMany({
|
||||
})
|
||||
|
||||
res.send(sponsors)
|
||||
})
|
||||
|
||||
app.post('/api/sponsors/create', async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
if(!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const user = await authorize(token)
|
||||
if(!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const { name, description, url, logo, banner } = req.body
|
||||
if(!name || !description || !url || !logo || !banner) return res.status(400).send(Errors.MISSING_ITEMS)
|
||||
|
||||
const sponsor = await prisma.sponsors.create({
|
||||
data: {
|
||||
name,
|
||||
description,
|
||||
url,
|
||||
}
|
||||
})
|
||||
|
||||
fs.writeFileSync(`/var/lib/rheinefuerrheine/sponsoren/${sponsor.ID}.logo`, logo)
|
||||
fs.writeFileSync(`/var/lib/rheinefuerrheine/sponsoren/${sponsor.ID}.banner`, banner)
|
||||
|
||||
res.send(sponsor)
|
||||
})
|
||||
|
||||
app.patch('/api/sponsors/edit/:id', async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
if(!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const user = await authorize(token)
|
||||
if(!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const { name, description, url, logo, banner } = req.body
|
||||
if(!name || !description || !url || !logo || !banner) return res.status(400).send(Errors.MISSING_ITEMS)
|
||||
|
||||
const sponsor = await prisma.sponsors.update({
|
||||
where: {
|
||||
ID: req.params.id
|
||||
},
|
||||
data: {
|
||||
name,
|
||||
description,
|
||||
url,
|
||||
}
|
||||
})
|
||||
|
||||
fs.writeFileSync(`/var/lib/rheinefuerrheine/sponsoren/${sponsor.ID}.logo`, logo)
|
||||
fs.writeFileSync(`/var/lib/rheinefuerrheine/sponsoren/${sponsor.ID}.banner`, banner)
|
||||
|
||||
res.send(sponsor)
|
||||
})
|
||||
|
||||
app.delete('/api/sponsors/delete/:id', async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
if(!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const user = await authorize(token)
|
||||
if(!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
await prisma.sponsors.delete({
|
||||
where: {
|
||||
ID: req.params.id
|
||||
}
|
||||
})
|
||||
|
||||
fs.rmSync(`/var/lib/rheinefuerrheine/sponsoren/${req.params.id}.logo`)
|
||||
fs.rmSync(`/var/lib/rheinefuerrheine/sponsoren/${req.params.id}.banner`)
|
||||
|
||||
res.send('OK')
|
||||
})
|
||||
|
||||
app.get('/api/users/all', async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
if(!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const user = await authorize(token)
|
||||
if(!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
if(!user.admin && !user.user_manage) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const users = await prisma.users.findMany({
|
||||
select: {
|
||||
ID: true,
|
||||
username: true,
|
||||
admin: true,
|
||||
article_create: true,
|
||||
article_manage: true,
|
||||
sponsor_manage: true,
|
||||
user_manage: true,
|
||||
}
|
||||
})
|
||||
|
||||
res.send(users)
|
||||
})
|
||||
|
||||
app.listen(process.env.PORT, () => {
|
||||
console.log(`Server is running on port ${process.env.PORT}`)
|
||||
})
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import { prisma } from '.'
|
||||
import crypto from 'crypto'
|
||||
import Logger from './logger';
|
||||
|
||||
const logger = new Logger('Init');
|
||||
|
||||
export default async function init() {
|
||||
logger.info('Checking database...');
|
||||
const users = await prisma.users.count();
|
||||
|
||||
if (users === 0) {
|
||||
logger.warn('No users found, creating admin user...');
|
||||
await prisma.$transaction([
|
||||
prisma.users.create({
|
||||
data: {
|
||||
username: 'admin',
|
||||
password: crypto.createHash('sha256').update('rheine admin rheine').digest('hex'),
|
||||
token: crypto.randomBytes(64).toString('hex'),
|
||||
admin: true
|
||||
}
|
||||
})
|
||||
]);
|
||||
|
||||
logger.info('Admin user created! Username: admin, Password: admin');
|
||||
}
|
||||
|
||||
logger.info('Database is ready!');
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
import chalk from "chalk";
|
||||
|
||||
export default class Logger {
|
||||
space: string;
|
||||
|
||||
static getTime(): string {
|
||||
const date = new Date();
|
||||
const hours = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
const seconds = date.getSeconds();
|
||||
|
||||
return `${hours < 10 ? '0' : ''}${hours}:${minutes < 10 ? '0' : ''}${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
|
||||
}
|
||||
|
||||
constructor(name: string) {
|
||||
this.space = name;
|
||||
}
|
||||
|
||||
public log(message: string): void {
|
||||
console.log(`[${Logger.getTime()}] [${this.space}] ${chalk.blue('[LOGS]')} ${message}`);
|
||||
}
|
||||
|
||||
public warn(message: string): void {
|
||||
console.log(`[${Logger.getTime()}] [${this.space}] ${chalk.yellow('[WARN]')} ${message}`);
|
||||
}
|
||||
|
||||
public error(message: string): void {
|
||||
console.log(`[${Logger.getTime()}] [${this.space}] ${chalk.red('[ERROR]')} ${message}`);
|
||||
}
|
||||
|
||||
public info(message: string): void {
|
||||
console.log(`[${Logger.getTime()}] [${this.space}] ${chalk.green('[INFO]')} ${message}`);
|
||||
}
|
||||
|
||||
public debug(message: string): void {
|
||||
if(!process.env.LOG_DEBUG) return;
|
||||
console.log(`[${Logger.getTime()}] [${this.space}] ${chalk.magenta('[DEBG]')} ${message}`);
|
||||
}
|
||||
|
||||
public fatal(message: string): void {
|
||||
console.log(chalk.bgRed(`[${Logger.getTime()}] [${this.space}] ${chalk.bold('[FATL]')} ${message}`));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
declare namespace Types {
|
||||
interface ErrorResponse {
|
||||
error: true
|
||||
type: string
|
||||
message: string
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "NodeNext", /* Specify what module code is generated. */
|
||||
"rootDir": "./src", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./dist", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
"noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
# Getting Started with Create React App
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
|
||||
The page will reload if you make edits.\
|
||||
You will also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||
|
||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"name": "frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@fontsource-variable/lexend": "^5.0.12",
|
||||
"@fontsource-variable/overpass": "^5.0.9",
|
||||
"@mui/icons-material": "^5.14.9",
|
||||
"@mui/material": "^5.14.10",
|
||||
"@types/node": "^16.18.52",
|
||||
"@types/react": "^18.2.22",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"axios": "^1.5.1",
|
||||
"js-sha256": "^0.10.1",
|
||||
"moment": "^2.29.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-intersection-observer": "^9.5.2",
|
||||
"react-router-dom": "^6.16.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"suneditor": "^2.45.1",
|
||||
"suneditor-react": "^3.6.1",
|
||||
"typescript": "^4.9.5",
|
||||
"zustand": "^4.4.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
After Width: | Height: | Size: 148 KiB |
Binary file not shown.
After Width: | Height: | Size: 224 KiB |
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 5.9 MiB |
Binary file not shown.
After Width: | Height: | Size: 192 KiB |
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
|
||||
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Rheine Für Rheine</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
After Width: | Height: | Size: 7.4 KiB |
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Loading</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
import { Routes, Route } from "react-router-dom";
|
||||
|
||||
// @Pages
|
||||
import LandingPage from "./pages/LandingPage";
|
||||
import LoginPage from "./pages/Login";
|
||||
import Datenschutz from "./pages/Datenschutz";
|
||||
import Impressum from "./pages/Impressum";
|
||||
import NotFound from "./pages/404";
|
||||
import AdminFrame from "./pages/AdminFrame";
|
||||
import Artikel from "./pages/Artikel";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<LandingPage />} />
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/datenschutz" element={<Datenschutz />} />
|
||||
<Route path="/impressum" element={<Impressum />} />
|
||||
<Route path="/admin/*" element={<AdminFrame />} />
|
||||
<Route path="*" element={<NotFound />} />
|
||||
<Route path="/artikel/:id" element={<Artikel />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
|
@ -0,0 +1,201 @@
|
|||
import { Grid, Typography } from "@mui/material";
|
||||
|
||||
export function AboutSection(): JSX.Element {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
|
||||
"@media (max-width: 1200px)": {
|
||||
pt: "50px",
|
||||
},
|
||||
}}
|
||||
id="about"
|
||||
>
|
||||
<Grid
|
||||
item
|
||||
lg={6}
|
||||
md={12}
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-start",
|
||||
gap: "10px",
|
||||
|
||||
px: "5%",
|
||||
}}
|
||||
>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: "bolder",
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "4rem",
|
||||
letterSpacing: "0.05rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "left",
|
||||
color: "primary.main",
|
||||
}}
|
||||
fontFamily="Lexend Variable"
|
||||
>
|
||||
Über uns
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: "light",
|
||||
fontFamily: "Overpass Variable",
|
||||
fontSize: "1.4rem",
|
||||
textAlign: "left",
|
||||
}}
|
||||
fontFamily="Overpass Variable"
|
||||
>
|
||||
Gemeinsam stark für Rheine – für die Rheinenser sein. Aus diesem Grund
|
||||
unterstützen wir die: Straßenparty – Rheines Stadtfest.
|
||||
<br /> <br />
|
||||
Wir möchten etwas bewegen, ein Zeichen setzen – wir möchten, dass die
|
||||
Rheinenser und die Besucher der Straßenparty ein tolles Programm und
|
||||
schöne musikalische Highlights genießen dürfen. Dafür haben wir uns
|
||||
zusammengetan.
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6} sx={{
|
||||
"@media (max-width: 1200px)": {
|
||||
display: "none",
|
||||
},
|
||||
}}>
|
||||
<Grid
|
||||
container
|
||||
gap={1}
|
||||
sx={{
|
||||
width: "100%",
|
||||
padding: "5%",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
item
|
||||
xs={5.5}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
|
||||
overflow: "hidden",
|
||||
|
||||
filter: "brightness(0.8)",
|
||||
"&:hover > :first-child": {
|
||||
transform: "scale(1.1)",
|
||||
filter: "brightness(1)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/assets/background1.jpg"
|
||||
alt="about-1"
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
objectFit: "contain",
|
||||
transition: "all 0.5s ease",
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={5.5}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: "primary.main",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{
|
||||
color: "#fff",
|
||||
fontWeight: "light",
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "4rem",
|
||||
textAlign: "center",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}>
|
||||
Unser <strong style={{
|
||||
fontWeight: "bold",
|
||||
marginTop: "-20px",
|
||||
}}>Rheine</strong>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={5.5}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: "primary.main"
|
||||
}}
|
||||
>
|
||||
<Typography sx={{
|
||||
color: "#fff",
|
||||
fontWeight: "light",
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "4rem",
|
||||
textAlign: "center",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}>
|
||||
Euer <strong style={{
|
||||
fontWeight: "bold",
|
||||
marginTop: "-20px",
|
||||
}}>Rheine</strong>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={5.5}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
|
||||
overflow: "hidden",
|
||||
|
||||
filter: "brightness(0.8)",
|
||||
"&:hover > :first-child": {
|
||||
transform: "scale(1.1)",
|
||||
filter: "brightness(1)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/assets/background2.jpg"
|
||||
alt="about-1"
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
objectFit: "contain",
|
||||
transition: "all 0.5s ease",
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,344 @@
|
|||
import { Box, Grid, Typography } from "@mui/material";
|
||||
import { Description as DescriptionIcon, Newspaper, Public } from "@mui/icons-material";
|
||||
|
||||
export function AboutSection2(): JSX.Element {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
id="info"
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
|
||||
py: "40px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
item
|
||||
xs={6}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "0px",
|
||||
|
||||
"@media (max-width: 1200px)": {
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "10%",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "auto",
|
||||
height: "70px",
|
||||
|
||||
px: "30px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
|
||||
backgroundColor: "primary.main",
|
||||
}}
|
||||
>
|
||||
<DescriptionIcon
|
||||
fontSize="large"
|
||||
sx={{
|
||||
color: "#fff",
|
||||
}} />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Overpass Variable",
|
||||
fontSize: "26px",
|
||||
fontWeight: "800",
|
||||
color: "#fff",
|
||||
}}
|
||||
>
|
||||
32.000
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Overpass Variable",
|
||||
fontSize: "12px",
|
||||
fontWeight: "light",
|
||||
color: "#fff",
|
||||
mt: "-5px",
|
||||
}}
|
||||
>
|
||||
Flyer der Straßenparty
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: "auto",
|
||||
height: "70px",
|
||||
|
||||
px: "30px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
|
||||
backgroundColor: "primary.main",
|
||||
}}
|
||||
>
|
||||
<Public
|
||||
fontSize="large"
|
||||
sx={{
|
||||
color: "#fff",
|
||||
}} />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Overpass Variable",
|
||||
fontSize: "26px",
|
||||
fontWeight: "800",
|
||||
color: "#fff",
|
||||
}}
|
||||
>
|
||||
www
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Overpass Variable",
|
||||
fontSize: "12px",
|
||||
fontWeight: "light",
|
||||
color: "#fff",
|
||||
mt: "-5px",
|
||||
}}
|
||||
>
|
||||
vonrheinefuerrheine.de
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "40px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "auto",
|
||||
height: "70px",
|
||||
|
||||
px: "30px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
|
||||
backgroundColor: "primary.main",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Overpass Variable",
|
||||
fontSize: "26px",
|
||||
fontWeight: "800",
|
||||
color: "#fff",
|
||||
}}
|
||||
>
|
||||
Hauptbühne
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Overpass Variable",
|
||||
fontSize: "12px",
|
||||
fontWeight: "light",
|
||||
color: "#fff",
|
||||
mt: "-5px",
|
||||
}}
|
||||
>
|
||||
Eröffnung & Logodarstellung
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "40px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "auto",
|
||||
height: "70px",
|
||||
|
||||
px: "30px",
|
||||
ml: "20%",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "15px",
|
||||
|
||||
backgroundColor: "primary.main",
|
||||
}}
|
||||
>
|
||||
<Newspaper
|
||||
fontSize="large"
|
||||
sx={{
|
||||
color: "#fff",
|
||||
}} />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Overpass Variable",
|
||||
fontSize: "26px",
|
||||
fontWeight: "800",
|
||||
color: "#fff",
|
||||
}}
|
||||
>
|
||||
Presse
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Overpass Variable",
|
||||
fontSize: "12px",
|
||||
fontWeight: "light",
|
||||
color: "#fff",
|
||||
mt: "-5px",
|
||||
}}
|
||||
>
|
||||
Verschiedene Berichte
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
md={12}
|
||||
xl={6}
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-end",
|
||||
|
||||
px: "5%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: "bolder",
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "4rem",
|
||||
letterSpacing: "0.05rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "right",
|
||||
color: "primary.main",
|
||||
}}
|
||||
fontFamily="Lexend Variable"
|
||||
>
|
||||
Mehrwert
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "3rem",
|
||||
letterSpacing: "0.05rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "right",
|
||||
color: "primary.main",
|
||||
|
||||
mb: "40px",
|
||||
}}
|
||||
fontFamily="Lexend Variable"
|
||||
>
|
||||
für alle Unterstützer
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: "light",
|
||||
fontFamily: "Overpass Variable",
|
||||
fontSize: "1.4rem",
|
||||
textAlign: "right",
|
||||
}}
|
||||
fontFamily="Overpass Variable"
|
||||
>
|
||||
Die Pandemie hat vielen Förderern der Stadt Rheine die Möglichkeit
|
||||
genommen zu unterstützen und wir, als regionale Dienstleister und
|
||||
Unternehmen, möchten wieder nach vorne schauen und Veranstaltungen für
|
||||
die Bürger für unsere Nachbarn, Kunden, Freunde und Bekannte
|
||||
ermöglichen. Teilnehmer können sich über folgendes freuen:
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
import {EngineeringOutlined } from "@mui/icons-material";
|
||||
import { Box, Button, Typography } from "@mui/material";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { FooterLink } from "./FooterLink";
|
||||
|
||||
export function Footer(): JSX.Element {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "250px",
|
||||
|
||||
mt: "50px",
|
||||
px: "20%",
|
||||
py: "50px",
|
||||
|
||||
backgroundColor: "primary.main",
|
||||
color: "#fff",
|
||||
|
||||
"@media (max-width: 1200px)": {
|
||||
px: "40px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
|
||||
gap: "3px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#fff",
|
||||
fontWeight: "bold",
|
||||
fontSize: "1.5rem",
|
||||
letterSpacing: "0.05rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "left",
|
||||
|
||||
mb: "10px",
|
||||
}}
|
||||
>
|
||||
Links
|
||||
</Typography>
|
||||
<Link
|
||||
to="/#about"
|
||||
onClick={() => {
|
||||
const element = document.getElementById("about");
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
className="nav-link-left"
|
||||
sx={{
|
||||
color: "#fff",
|
||||
fontWeight: "light",
|
||||
fontSize: "1.25rem",
|
||||
letterSpacing: "0.05rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
Über uns
|
||||
</Typography>
|
||||
</Link>
|
||||
<Link
|
||||
to="/#info"
|
||||
onClick={() => {
|
||||
const element = document.getElementById("info");
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
className="nav-link-left"
|
||||
sx={{
|
||||
color: "#fff",
|
||||
fontWeight: "light",
|
||||
fontSize: "1.25rem",
|
||||
letterSpacing: "0.05rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
Für Sie
|
||||
</Typography>
|
||||
</Link>
|
||||
<Link
|
||||
to="/#sponsors"
|
||||
onClick={() => {
|
||||
const element = document.getElementById("sponsors");
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
className="nav-link-left"
|
||||
sx={{
|
||||
color: "#fff",
|
||||
fontWeight: "light",
|
||||
fontSize: "1.25rem",
|
||||
letterSpacing: "0.05rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
Sponsoren
|
||||
</Typography>
|
||||
</Link>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
|
||||
gap: "3px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#fff",
|
||||
fontWeight: "bold",
|
||||
fontSize: "1.5rem",
|
||||
letterSpacing: "0.05rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "left",
|
||||
|
||||
mb: "10px",
|
||||
}}
|
||||
>
|
||||
Rechtliches
|
||||
</Typography>
|
||||
<FooterLink to="/datenschutz">Datenschutz</FooterLink>
|
||||
<FooterLink to="/impressum">Impressum</FooterLink>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#fff",
|
||||
fontWeight: "light",
|
||||
fontSize: "1rem",
|
||||
letterSpacing: "0.05rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
© 2023 <br /> IPGSystems inc. All rights reserved
|
||||
</Typography>
|
||||
<Button
|
||||
onClick={() => navigate("/login")}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
startIcon={<EngineeringOutlined />}
|
||||
sx={{
|
||||
ml: "auto",
|
||||
position: "absolute",
|
||||
right: "20px",
|
||||
color: "#fff",
|
||||
"&:hover": {
|
||||
transform: "scale(1.1)",
|
||||
},
|
||||
|
||||
}}
|
||||
>
|
||||
|
||||
Admin
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import { Typography } from "@mui/material";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export function FooterLink({
|
||||
to, children,
|
||||
}: {
|
||||
to: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return <Link to={to}>
|
||||
<Typography
|
||||
className="nav-link-left"
|
||||
sx={{
|
||||
color: "#fff",
|
||||
fontWeight: "light",
|
||||
fontSize: "1.25rem",
|
||||
letterSpacing: "0.05rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Typography>
|
||||
</Link>;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import { Typography } from "@mui/material";
|
||||
import { CSSProperties } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
function NavLink({
|
||||
children,
|
||||
to,
|
||||
style,
|
||||
}: {
|
||||
children: string;
|
||||
to: string;
|
||||
style?: CSSProperties;
|
||||
}) {
|
||||
return (
|
||||
<Link
|
||||
to={to}
|
||||
style={style}
|
||||
onClick={() => {
|
||||
const element = document.getElementById(to.replaceAll("#", ""));
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Typography fontSize="20px" fontWeight="bold" className="nav-link">
|
||||
{children}
|
||||
</Typography>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavLink;
|
|
@ -0,0 +1,57 @@
|
|||
import { Box, Divider } from "@mui/material"
|
||||
import SidebarElement from "./SidebarElement"
|
||||
import { Home, Newspaper, Person, Savings } from "@mui/icons-material"
|
||||
|
||||
function Sidebar() {
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
width: "250px",
|
||||
height: "100vh",
|
||||
position: "fixed",
|
||||
top: "0",
|
||||
left: "0",
|
||||
|
||||
backgroundColor: "#fff",
|
||||
boxShadow: "0 0 10px #00000055",
|
||||
|
||||
zIndex: 1000,
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
|
||||
py: "10px",
|
||||
}}>
|
||||
<img src="/logo.png" alt="logo" style={{
|
||||
width: "80%",
|
||||
marginBottom: "10px",
|
||||
}} />
|
||||
|
||||
<Divider sx={{
|
||||
width: "100%",
|
||||
height: "1px",
|
||||
|
||||
mb: "5px",
|
||||
}} />
|
||||
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "2px",
|
||||
}}>
|
||||
<SidebarElement Title="Dashboard" Icon={Home} Path="/admin/dashboard" />
|
||||
<SidebarElement Title="Artikel" Icon={Newspaper} Path="/admin/artikel" />
|
||||
<SidebarElement Title="Sponsoren" Icon={Savings} Path="/admin/sponsoren" />
|
||||
<SidebarElement Title="Benutzer" Icon={Person} Path="/admin/benutzer" />
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default Sidebar
|
|
@ -0,0 +1,64 @@
|
|||
import { Box, SvgIconTypeMap, Typography } from "@mui/material";
|
||||
import { OverridableComponent } from "@mui/material/OverridableComponent";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
function SidebarElement({
|
||||
Title,
|
||||
Icon,
|
||||
Path,
|
||||
}: {
|
||||
Title: string;
|
||||
Icon: OverridableComponent<SvgIconTypeMap<{}, "svg">> & {
|
||||
muiName: string;
|
||||
};
|
||||
Path: string;
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "50px",
|
||||
|
||||
cursor: "pointer",
|
||||
backgroundColor: theme => location.pathname === Path ? theme.palette.primary.light : "#00000011",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
gap: "15px",
|
||||
px: "20px",
|
||||
|
||||
transition: "all 0.2s ease-in-out",
|
||||
|
||||
"&:hover": {
|
||||
gap: "25px",
|
||||
background: (theme) => theme.palette.primary.main,
|
||||
},
|
||||
}}
|
||||
onClick={() => navigate(Path)}
|
||||
>
|
||||
<Icon
|
||||
sx={{
|
||||
fontSize: "30px",
|
||||
color: "#000000CC",
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "20px",
|
||||
fontWeight: "bold",
|
||||
color: "#000000CC",
|
||||
}}
|
||||
>
|
||||
{Title}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default SidebarElement;
|
|
@ -0,0 +1,157 @@
|
|||
import { Avatar, Box, Typography } from "@mui/material";
|
||||
import { useRef, useState } from "react";
|
||||
|
||||
function SponsorCard() {
|
||||
const logoRef = useRef<HTMLInputElement>(null);
|
||||
const bannerRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [logo, setLogo] = useState<string>("");
|
||||
const [banner, setBanner] = useState<string>("https://placehold.co/500x150");
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="file"
|
||||
ref={logoRef}
|
||||
accept="image/*"
|
||||
style={{ display: "none" }}
|
||||
onChange={(e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
setLogo(reader.result as string);
|
||||
};
|
||||
}}
|
||||
onAbort={() => {}}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="file"
|
||||
ref={bannerRef}
|
||||
accept="image/*"
|
||||
style={{ display: "none" }}
|
||||
onChange={(e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
setBanner(reader.result as string);
|
||||
};
|
||||
}}
|
||||
onAbort={() => {}}
|
||||
required
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "500px",
|
||||
height: "300px",
|
||||
|
||||
backgroundColor: "#fff",
|
||||
|
||||
// clip the edges using a clip path
|
||||
clipPath: "polygon(0 0, 100% 0, 100% 100%, 0% 100%)",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={banner}
|
||||
alt="banner"
|
||||
style={{
|
||||
width: "500px",
|
||||
height: "150px",
|
||||
|
||||
objectFit: "cover",
|
||||
minWidth: "500px",
|
||||
minHeight: "150px",
|
||||
|
||||
cursor: "crosshair",
|
||||
}}
|
||||
onClick={() => bannerRef.current?.click()}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
mt: "-75px",
|
||||
|
||||
width: "500px",
|
||||
height: "225px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
variant="square"
|
||||
src={logo}
|
||||
sx={{
|
||||
width: "150px",
|
||||
height: "150px",
|
||||
|
||||
borderRadius: "20px",
|
||||
ml: "25px",
|
||||
|
||||
cursor: "crosshair",
|
||||
}}
|
||||
onClick={() => {
|
||||
logoRef.current?.click();
|
||||
}}
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "300px",
|
||||
height: "175px",
|
||||
background: "#fff",
|
||||
|
||||
mt: "35px",
|
||||
ml: "25px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
|
||||
borderTopLeftRadius: "13px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
contentEditable
|
||||
sx={{
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "38px",
|
||||
fontWeight: 800,
|
||||
fontStyle: "italic",
|
||||
ml: "10px",
|
||||
}}
|
||||
>
|
||||
Felix Orgel
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
contentEditable
|
||||
sx={{
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "16px",
|
||||
fontWeight: 200,
|
||||
fontStyle: "italic",
|
||||
color: "#828282",
|
||||
ml: "10px",
|
||||
}}
|
||||
>
|
||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
|
||||
nonumy eirmod tempor invidunt u
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default SponsorCard;
|
|
@ -0,0 +1,80 @@
|
|||
import { Close } from "@mui/icons-material";
|
||||
import {
|
||||
Backdrop,
|
||||
Box,
|
||||
IconButton,
|
||||
TextField,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import SponsorCard from "./SponsorCard";
|
||||
|
||||
function SponsorModal({
|
||||
id,
|
||||
onClose,
|
||||
}: {
|
||||
id: string | null;
|
||||
onClose?: () => void;
|
||||
}) {
|
||||
if (!id) return <></>;
|
||||
return (
|
||||
<Backdrop
|
||||
open={true}
|
||||
sx={{
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "600px",
|
||||
height: "auto",
|
||||
padding: "10px",
|
||||
|
||||
backgroundColor: "#fff",
|
||||
boxShadow: "0px 0px 10px #00000033",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "40px",
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "28px",
|
||||
fontWeight: 700,
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
Sponsor Erstellen
|
||||
</Typography>
|
||||
<IconButton onClick={onClose}>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
<SponsorCard />
|
||||
</Box>
|
||||
</Backdrop>
|
||||
);
|
||||
}
|
||||
|
||||
export default SponsorModal;
|
|
@ -0,0 +1,169 @@
|
|||
import { AppBar, Box, Button, Drawer, useTheme } from "@mui/material";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Menu, DescriptionOutlined } from "@mui/icons-material";
|
||||
import { CSSProperties, useState } from "react";
|
||||
|
||||
// @Component
|
||||
import NavLink from "./NavLink";
|
||||
|
||||
|
||||
const MobileLinkStyle: CSSProperties = {
|
||||
height: "60px",
|
||||
width: '100%',
|
||||
|
||||
borderBottom: "1px solid #000",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}
|
||||
|
||||
function TopBar() {
|
||||
const theme = useTheme();
|
||||
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppBar
|
||||
position="static"
|
||||
sx={{
|
||||
height: "100px",
|
||||
backgroundColor: "#ffffff",
|
||||
color: "#ffffff",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
|
||||
px: "15%",
|
||||
|
||||
boxShadow: "3px 2px 2px rgba(0, 0, 0, 0.25)",
|
||||
|
||||
"@media screen and (max-width: 768px)": {
|
||||
px: "0px",
|
||||
},
|
||||
|
||||
"@media screen and (max-width: 1300px)": {
|
||||
px: "2px",
|
||||
},
|
||||
|
||||
"@media screen and (max-width: 1800px)": {
|
||||
px: "10%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Link to="/">
|
||||
<img
|
||||
src="/logo.png"
|
||||
alt="logo"
|
||||
style={{
|
||||
width: "227px",
|
||||
height: "70px",
|
||||
}}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
flexGrow: 0,
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: "50px",
|
||||
|
||||
"@media screen and (max-width: 768px)": {
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<NavLink to="/#about">Über uns</NavLink>
|
||||
<NavLink to="/#info">Für Sie</NavLink>
|
||||
<NavLink to="/#sponsors">Sponsoren</NavLink>
|
||||
</Box>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
startIcon={<DescriptionOutlined />}
|
||||
sx={{
|
||||
color: "#ffffff",
|
||||
|
||||
transition: "all 0.3s ease-in-out",
|
||||
|
||||
borderRadius: "none",
|
||||
|
||||
"&:hover": {
|
||||
transform: "scale(1.1)",
|
||||
},
|
||||
|
||||
"@media screen and (max-width: 1000px)": {
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Download Flyer
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
|
||||
backgroundColor: "transparent",
|
||||
border: "none",
|
||||
backdropFilter: "none",
|
||||
boxShadow: "none",
|
||||
|
||||
"&:hover": {
|
||||
backgroundColor: "transparent",
|
||||
border: "none",
|
||||
boxShadow: `3px 3px 3px 0px ${theme.palette.secondary.main}`,
|
||||
},
|
||||
|
||||
"@media screen and (min-width: 768px)": {
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
onClick={() => setSidebarOpen(true)}
|
||||
>
|
||||
<Menu />
|
||||
</Button>
|
||||
</AppBar>
|
||||
<Drawer
|
||||
anchor="right"
|
||||
open={sidebarOpen}
|
||||
onClose={() => setSidebarOpen(false)}
|
||||
sx={{
|
||||
"@media screen and (min-width: 768px)": {
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
width: 250,
|
||||
overflow: 'auto',
|
||||
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
gap: '0px',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
},
|
||||
onClick: () => setSidebarOpen(false),
|
||||
}}
|
||||
>
|
||||
<NavLink to="#about" style={MobileLinkStyle}>Über uns</NavLink>
|
||||
<NavLink to="#info" style={MobileLinkStyle}>Für Sie</NavLink>
|
||||
<NavLink to="#sponsors" style={MobileLinkStyle}>Sponsoren</NavLink>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default TopBar;
|
|
@ -0,0 +1,12 @@
|
|||
export function getBaseURL(): string {
|
||||
if(process.env.NODE_ENV === "development") return "http://localhost:3001"
|
||||
else return ""
|
||||
}
|
||||
|
||||
export function resizeIframe(obj: HTMLIFrameElement) {
|
||||
obj.style.height = (obj.contentWindow as any).document.documentElement.scrollHeight + 'px';
|
||||
}
|
||||
|
||||
export function formatNum(num: number) {
|
||||
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-x: hidden;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
span, label {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
@keyframes gradientAnim {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
.nav-link {
|
||||
color: #005487;
|
||||
font-weight: bold;
|
||||
font-size: 25px;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0.05em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease 0s;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
.nav-link::after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
|
||||
background: #005487;
|
||||
transition: width 0.3s ease 0s;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.nav-link:hover::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link-left {
|
||||
color: #005487;
|
||||
font-weight: bold;
|
||||
font-size: 25px;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0.05em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease 0s;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
.nav-link-left::after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
|
||||
background: #fff;
|
||||
transition: width 0.3s ease 0s;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.nav-link-left:hover::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.se-resizing-bar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.sun-editor-editable {
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.se-toolbar{
|
||||
position: sticky !important;
|
||||
flex-direction: column !important;
|
||||
|
||||
width: 100px !important;
|
||||
transform: translate(-105px);
|
||||
z-index: 0 !important;
|
||||
}
|
||||
|
||||
.se-btn-tray {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
align-items: flex-start !important;
|
||||
gap: 2px !important;
|
||||
}
|
||||
|
||||
.se-wrapper {
|
||||
margin-top: -480px;
|
||||
}
|
||||
|
||||
.articleBanner {
|
||||
transition: ease 0.3s;
|
||||
}
|
||||
.articleBanner:hover {
|
||||
filter: brightness(0.7);
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import ReactDOM from "react-dom/client";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import { createTheme, ThemeProvider, CssBaseline } from "@mui/material";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
// fonts
|
||||
import '@fontsource-variable/overpass';
|
||||
import '@fontsource-variable/lexend';
|
||||
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#6f9c01",
|
||||
},
|
||||
secondary: {
|
||||
main: "#00ACD3",
|
||||
},
|
||||
},
|
||||
// make the buttons not rounded
|
||||
components: {
|
||||
MuiButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: 0,
|
||||
fontFamily: "Lexend Variable",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export { theme };
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById("root") as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</ThemeProvider>
|
||||
);
|
|
@ -0,0 +1,87 @@
|
|||
import { Box, Button, Typography } from "@mui/material";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {HouseOutlined } from "@mui/icons-material";
|
||||
|
||||
|
||||
function NotFound() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
|
||||
background: `linear-gradient(45deg, #0022ff55, #00AA0099)`,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
fontSize: "10rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "right",
|
||||
|
||||
background: `linear-gradient(45deg, #0022ff, #00AA00)`,
|
||||
backgroundClip: "text",
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
|
||||
color: "transparent",
|
||||
}}
|
||||
>
|
||||
404
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: "light",
|
||||
fontSize: "2rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "center",
|
||||
|
||||
background: `linear-gradient(45deg, #0022ff, #00AA00)`,
|
||||
backgroundClip: "text",
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
|
||||
color: "transparent",
|
||||
}}
|
||||
>
|
||||
Seite nicht gefunden
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
startIcon={< HouseOutlined />}
|
||||
sx={{
|
||||
mt: "2rem",
|
||||
color: "#fff",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate("/");
|
||||
}}
|
||||
>
|
||||
Zurück zur Startseite
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default NotFound;
|
|
@ -0,0 +1,43 @@
|
|||
import { Box } from "@mui/material";
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import Sidebar from "../components/Sidebar";
|
||||
import Dashboard from "./admin/Dashboard";
|
||||
import Artikel from "./admin/Artikel";
|
||||
import Sponsoren from "./admin/Sponsoren";
|
||||
import Benutzer from "./admin/Benutzer";
|
||||
import ArticleEditor from "./admin/ArticleEditor";
|
||||
|
||||
function AdminFrame() {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Sidebar />
|
||||
<Box sx={{
|
||||
marginLeft: "255px",
|
||||
width: "calc(100% - 255px)",
|
||||
}}>
|
||||
<Routes>
|
||||
<Route path="/dashboard" element={<Dashboard />} />
|
||||
<Route path="/artikel" element={<Artikel />} />
|
||||
<Route path="/sponsoren" element={<Sponsoren />} />
|
||||
<Route path="/benutzer" element={<Benutzer />} />
|
||||
<Route path="/editor" element={<ArticleEditor />} />
|
||||
<Route path="/editor/:id" element={<ArticleEditor />} />
|
||||
</Routes>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default AdminFrame;
|
|
@ -0,0 +1,35 @@
|
|||
object table {
|
||||
border: 1px solid #e1e1e1;
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
object table td {
|
||||
border: 1px solid #e1e1e1;
|
||||
}
|
||||
|
||||
object figure {
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
object pre {
|
||||
background: #f9f9f9;
|
||||
padding: 8px;
|
||||
color: #666;
|
||||
line-height: 1.45;
|
||||
border: 1px solid #e1e1e1;
|
||||
border-radius: 2px;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
object {
|
||||
font-family: 'Lexend Variable';
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.se-toolbar-sticky-dummy {
|
||||
height: 0 !important;
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/* eslint-disable jsx-a11y/alt-text */
|
||||
import { useParams } from "react-router-dom";
|
||||
import TopBar from "../components/TopBar";
|
||||
import { Box, CircularProgress, Grid, Typography } from "@mui/material";
|
||||
import { getBaseURL } from "../functions";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
import "./Artikel.css";
|
||||
|
||||
function Artikel() {
|
||||
const ref = useRef<HTMLObjectElement>(null);
|
||||
const { id } = useParams() as {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const [loadingContent, setLoadingContent] = useState(true);
|
||||
const [article, setArticle] = useState<Types.Article | null>(null);
|
||||
const [banner, setBanner] = useState<string>("");
|
||||
|
||||
const loadbanner = () => {
|
||||
axios
|
||||
.get(`${getBaseURL()}/api/article/banner/${id}`)
|
||||
.then((response) => {
|
||||
setBanner(response.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
setBanner("https://placehold.co/1920x1080");
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
loadbanner();
|
||||
|
||||
axios.get(`${getBaseURL()}/api/article/content/${id}`).then((response) => {
|
||||
if (ref.current) {
|
||||
ref.current.innerHTML = response.data;
|
||||
setLoadingContent(false);
|
||||
}
|
||||
});
|
||||
|
||||
axios.get(`${getBaseURL()}/api/article/view/${id}`).then((response) => {
|
||||
setArticle(response.data);
|
||||
setTimeout(() => {
|
||||
// get all images with a data-size property
|
||||
const images = document.querySelectorAll("img[data-size]");
|
||||
console.log(images);
|
||||
|
||||
// loop through all images and set the width style to the data-size property
|
||||
for (let image of [...images]) {
|
||||
(image as HTMLElement).style.width =
|
||||
image.getAttribute("data-size")?.replaceAll(",", "") ?? "auto";
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopBar />
|
||||
{loadingContent && <CircularProgress />}
|
||||
{!loadingContent && (
|
||||
<Box sx={{
|
||||
height: "100vh",
|
||||
width: "100vw",
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
left: 0,
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
zIndex: -1,
|
||||
}}>
|
||||
<img
|
||||
src={banner}
|
||||
alt=""
|
||||
style={{
|
||||
height: "100vh",
|
||||
width: "100vw",
|
||||
position: "absolute",
|
||||
top: "0px",
|
||||
zIndex: -1,
|
||||
filter: "brightness(0.5)",
|
||||
objectFit: "cover",
|
||||
minWidth: "100vw",
|
||||
minHeight: "100vh",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
|
||||
px: "15%",
|
||||
|
||||
"@media screen and (max-width: 768px)": {
|
||||
px: "0px",
|
||||
},
|
||||
|
||||
"@media screen and (max-width: 1300px)": {
|
||||
px: "2px",
|
||||
},
|
||||
|
||||
"@media screen and (min-width: 1800px)": {
|
||||
px: "15%",
|
||||
},
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
|
||||
transform: "translateY(25vh)",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "1080px",
|
||||
height: "fit-content",
|
||||
boxShadow: "0px 0px 10px 0px rgba(0,0,0,0.5)",
|
||||
opacity: loadingContent ? 0 : 1,
|
||||
padding: "40px",
|
||||
backgroundColor: "white",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h1"
|
||||
sx={{
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "3rem",
|
||||
fontWeight: 700,
|
||||
textAlign: "left",
|
||||
mb: "20px",
|
||||
}}
|
||||
>
|
||||
{article?.title}
|
||||
</Typography>
|
||||
|
||||
<Grid container sx={{}}></Grid>
|
||||
|
||||
<object
|
||||
id="article-content"
|
||||
height="fit-content"
|
||||
type="text/html"
|
||||
ref={ref}
|
||||
style={{
|
||||
width: "1000px",
|
||||
height: "fit-content",
|
||||
border: "none",
|
||||
padding: "0px",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Artikel;
|
|
@ -0,0 +1,317 @@
|
|||
import { Box, Divider, SxProps, Theme, Typography } from "@mui/material";
|
||||
import TopBar from "../components/TopBar";
|
||||
import { Footer } from "../components/Footer";
|
||||
import { useEffect } from "react";
|
||||
|
||||
let dividerStyle: SxProps<Theme> = {
|
||||
my: "20px",
|
||||
width: "100%",
|
||||
background: (theme) => theme.palette.secondary.main,
|
||||
};
|
||||
|
||||
function Datenschutz() {
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopBar />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
px: "10vw",
|
||||
mt: "5vh",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: "800",
|
||||
mb: "5vh",
|
||||
fontSize: "3rem",
|
||||
}}
|
||||
>
|
||||
Datenschutzerklärung
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: "light",
|
||||
mb: "5vh",
|
||||
fontSize: "1rem",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<strong>Verantwortlicher</strong>
|
||||
<br />
|
||||
Verantwortlicher für diese Internetseiten ist RF Computer GmbH & Co.
|
||||
KG., Otto-Bergmeyer-Str. 7, 48431 Rheine. Weitere Angaben zu unserem
|
||||
Unternehmen und den vertretungsberechtigten Personen können Sie
|
||||
unserem Impressum entnehmen.
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong> Welche Daten werden verarbeitet</strong>
|
||||
<br />
|
||||
Rechtsgrundlagen der Datenverarbeitung Um Ihnen unsere Internetseite
|
||||
und die damit verbundenen Dienstleistungen anbieten zu können,
|
||||
verarbeiten wir personenbezogene Daten auf Basis folgender
|
||||
Rechtsgrundlagen:
|
||||
<ul>
|
||||
<li>Einwilligung (Art. 6 Abs. 1 lit. a) DSGVO</li>
|
||||
<li>zur Erfüllung von Verträgen (Art. 6 Abs. 1 lit. b) DSGVO</li>
|
||||
<li>
|
||||
auf Basis einer Interessenabwägung (Art. 6 Abs. 1 lit. f) DSGVO
|
||||
</li>
|
||||
<li>
|
||||
zur Erfüllung einer rechtlichen Verpflichtung (Art. 6 Abs. 1 lit.
|
||||
c) DSGVO
|
||||
</li>
|
||||
</ul>
|
||||
Wir werden im Zusammenhang mit der jeweiligen Verarbeitung auf die
|
||||
entsprechenden Begrifflichkeiten Bezug nehmen, so dass Sie einordnen
|
||||
können, auf welcher Basis wir personenbezogene Daten verarbeiten.
|
||||
<br />
|
||||
<br />
|
||||
Wenn personenbezogene Daten auf Grundlage einer Einwilligung von Ihnen
|
||||
verarbeitet werden, haben Sie das Recht, die Einwilligung jederzeit
|
||||
mit Wirkung für die Zukunft uns gegenüber zu widerrufen.
|
||||
<br />
|
||||
<br />
|
||||
Wenn wir Daten auf Basis einer Interessenabwägung verarbeiten, haben
|
||||
Sie als Betroffene/r das Recht, unter Berücksichtigung der Vorgaben
|
||||
von Art. 21 DSGVO der Verarbeitung der personenbezogenen Daten zu
|
||||
widersprechen.
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong> Zugriffsdaten </strong>
|
||||
<br />
|
||||
Wenn Sie unsere Internetseiten besuchen, werden personenbezogene Daten
|
||||
verarbeitet, um Ihnen die Inhalte der Internetseite auf Ihrem Endgerät
|
||||
anzeigen zu können.
|
||||
<br />
|
||||
<br />
|
||||
Damit die Seiten in Ihrem Browser dargestellt werden können, muss die
|
||||
IP-Adresse des von Ihnen verwendeten Endgeräts verarbeitet werden.
|
||||
Hinzu kommen weitere Information über den Browser Ihres Endgeräts.
|
||||
<br />
|
||||
<br />
|
||||
Wir sind datenschutzrechtlich verpflichtet, auch die Vertraulichkeit
|
||||
und Integrität der mit unseren IT-Systemen verarbeiteten
|
||||
personenbezogenen Daten zu gewährleisten.
|
||||
<br />
|
||||
<br />
|
||||
Für diesen Zweck und aus diesem Interesse werden auf Basis einer
|
||||
Interessenabwägung nachfolgende Daten protokolliert:
|
||||
<br />
|
||||
<br />
|
||||
<ul>
|
||||
<li>IP-Adresse des aufrufenden Rechners (für maximal 7 Tage)</li>
|
||||
<li>Betriebssystem des aufrufenden Rechners</li>
|
||||
<li>Browser Version des aufrufenden Rechners</li>
|
||||
<li>Name der abgerufenen Datei</li>
|
||||
<li>Datum und Uhrzeit des Abrufs</li>
|
||||
<li>Übertragene Datenmenge</li>
|
||||
<li>Verweisende URL</li>
|
||||
</ul>
|
||||
Die IP-Adresse wird nach spätestens 7 Tagen von allen Systemen, die im
|
||||
Zusammenhang mit dem Betrieb dieser Internetseiten verwendet werden,
|
||||
gelöscht. Einen Personenbezug können wir aus den verbleibenden Daten
|
||||
dann nicht mehr herstellen.
|
||||
<br />
|
||||
<br />
|
||||
Die Daten werden ferner auch verwendet, um Fehler auf den
|
||||
Internetseiten ermitteln und beheben zu können.
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong> Kontaktformular </strong>
|
||||
<br />
|
||||
Wir bieten auf unserer Internetseite ein Kontaktformular an, über das
|
||||
Sie Informationen zu unseren Produkten oder Dienstleistungen anfordern
|
||||
oder allgemein Kontakt aufnehmen können. Die von Ihnen zwingend zur
|
||||
Beantwortung einer Anfrage erforderlichen Daten haben wir als
|
||||
Pflichtfelder gekennzeichnet. Angaben zu weiteren Datenfeldern sind
|
||||
freiwillig.
|
||||
<br />
|
||||
<br />
|
||||
Wir benötigen diese Angaben, um Ihre Anfrage zu bearbeiten, Sie
|
||||
korrekt anzusprechen und Ihnen eine Antwort zukommen zu lassen. Die
|
||||
Datenverarbeitung erfolgt bei konkreten Anfragen zur Erfüllung eines
|
||||
Vertrages bzw. der Vertragsanbahnung. Bei allgemeinen Anfragen erfolgt
|
||||
die Verarbeitung auf Basis einer Interessenabwägung.
|
||||
<br />
|
||||
<br />
|
||||
Wir benötigen diese Angaben, um Ihre Anfrage zu bearbeiten, Sie
|
||||
korrekt anzusprechen und Ihnen eine Antwort zukommen zu lassen. Die
|
||||
Datenverarbeitung erfolgt bei konkreten Anfragen zur Erfüllung eines
|
||||
Vertrages bzw. der Vertragsanbahnung. Bei allgemeinen Anfragen erfolgt
|
||||
die Verarbeitung auf Basis einer Interessenabwägung.
|
||||
<br />
|
||||
<br />
|
||||
Anfragen, die über das Kontaktformular unserer Internetseite eingehen,
|
||||
werden bei uns elektronisch verarbeitet, um Ihre Anfrage zu
|
||||
beantworten. In dem Zusammenhang erhalten ggf. auch weitere Personen
|
||||
oder Abteilungen und ggf. Dritte Kenntnis von den Formularinhalten,
|
||||
die Sie übersendet haben.
|
||||
<br />
|
||||
<br />
|
||||
Die Übermittlung der Formular-Daten über das Internet erfolgt über
|
||||
verschlüsselte Verbindungen.
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong> Newsletter </strong>
|
||||
Sie können auf unserer Internetseite auch einen E-Mail-Newsletter
|
||||
abonnieren. Wir verarbeiten neben den freiwilligen Angaben im
|
||||
jeweiligen Formular dazu nur Ihre E-Mail-Adresse. Diese ist allerdings
|
||||
auch zwingend erforderlich, um Ihnen den Newsletter zusenden zu
|
||||
können.
|
||||
<br />
|
||||
<br />
|
||||
Sie können den Newsletter jederzeit bei uns abbestellen. Alternativ
|
||||
finden Sie einen Link zur Abmeldung in jeder Newsletter-E-Mail.
|
||||
<br />
|
||||
<br />
|
||||
Um die Beliebtheit von unseren Newsletter-Aussendungen analysieren zu
|
||||
können und diese optimieren zu können, protokollieren wir, wenn
|
||||
E-Mails geöffnet und Links geklickt wurden. Diese Nutzungsanalyse
|
||||
erfolgt auf Basis einer Interessenabwägung. Sie können dieser
|
||||
Verarbeitung widersprechen, indem Sie sich von dem Newsletter
|
||||
abmelden.
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong> Cookies </strong>
|
||||
<br />
|
||||
Auf unseren Internetseiten kommen Cookies zum Einsatz. Cookies sind
|
||||
kleine Textinformationen, die über ihren Browser in Ihrem Endgerät
|
||||
gespeichert werden. Die Cookies sind erforderlich, um bestimmte
|
||||
Funktionen unserer Internetseiten zu ermöglichen. Wir nutzen dabei nur
|
||||
Session-Cookies, die unmittelbar nach Beendigung des Besuchs der
|
||||
Internetseiten automatisch von Ihrem Browser gelöscht werden.
|
||||
<br />
|
||||
<br />
|
||||
Der Einsatz von Cookies erfolgt auf Basis einer Interessenabwägung.
|
||||
Unser Interesse ist der bedienungsfreundliche Besuch unserer
|
||||
Internetseiten.
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong> Zwecke der Verarbeitung personenbezogener Daten </strong>
|
||||
<br />
|
||||
Die vorgenannten Daten verarbeiten wir für den Betrieb unserer
|
||||
Internetseite und für Erfüllung von vertraglichen Pflichten gegenüber
|
||||
unseren Kunden bzw. der Wahrung unserer berechtigten Interessen.
|
||||
<br />
|
||||
<br />
|
||||
Bei Anfragen von Ihnen außerhalb eines aktiven Kundenverhältnisses
|
||||
verarbeiten wir die Daten für Zwecke des Vertriebs und der Werbung.
|
||||
Sie können einer Verwendung Ihrer personenbezogenen Daten für
|
||||
Werbezwecke jederzeit widersprechen.
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong> Freiwillige Angaben </strong>
|
||||
<br />
|
||||
Soweit Sie Daten uns gegenüber z.B. in Formularen freiwillig angeben
|
||||
und diese für die Erfüllung unserer vertraglichen Pflichten nicht
|
||||
erforderlich sind, verarbeiten wir diese Daten in der berechtigten
|
||||
Annahme, dass die Verarbeitung und Verwendung dieser Daten in Ihrem
|
||||
Interesse ist.
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong> Empfänger / Weitergabe von Daten </strong>
|
||||
<br />
|
||||
Daten, die Sie uns gegenüber angeben, werden grundsätzlich nicht an
|
||||
Dritte weitergegeben. Insbesondere werden Ihre Daten nicht an Dritte
|
||||
für deren Werbezwecke weitergegeben.
|
||||
<br />
|
||||
<br />
|
||||
Wir setzen jedoch ggf. Dienstleister für den Betrieb dieser
|
||||
Internetseiten oder für weitere Produkte oder Dienstleistungen von uns
|
||||
ein. Hier kann es vorkommen, dass ein Dienstleister Kenntnis von
|
||||
personenbezogenen Daten erhält Wir wählen unsere Dienstleister
|
||||
sorgfältig – insbesondere im Hinblick auf Datenschutz und
|
||||
Datensicherheit – aus und treffen alle datenschutzrechtlich
|
||||
erforderlichen Maßnahmen für eine zulässige Datenverarbeitung.
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong> Datenverarbeitung außerhalb Europäischer Union </strong>
|
||||
Soweit personenbezogene Daten außerhalb der europäischen Union
|
||||
verarbeitet werden, können Sie dies den vorherigen Ausführungen
|
||||
entnehmen.
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong>Datenschutzbeauftragter </strong>
|
||||
<br />
|
||||
Wir haben einen Datenschutzbeauftragten benannt.
|
||||
<br />
|
||||
<br />
|
||||
Sie erreichen diesen wie folgt:
|
||||
<br />
|
||||
<br />
|
||||
Aurora Data GmbH
|
||||
<br />
|
||||
Gebrüder-Schönthal-Str. 37
|
||||
<br />
|
||||
48432 Rheine
|
||||
<br />
|
||||
<br />
|
||||
Tel. 05975 955 8 455
|
||||
<br />
|
||||
E-Mail: datenschutz@aurora-data.de
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong>Ihre Rechte als Betroffener</strong>
|
||||
<br />
|
||||
Sie haben das Recht auf Auskunft über die Sie betreffenden
|
||||
personenbezogenen Daten. Sie können sich für eine Auskunft jederzeit
|
||||
an uns wenden.
|
||||
<br />
|
||||
<br />
|
||||
Bei einer Auskunftsanfrage, die nicht schriftlich erfolgt, bitten wir
|
||||
um Verständnis dafür, dass wir ggf. Nachweise von Ihnen verlangen, die
|
||||
belegen, dass Sie die Person sind, für die Sie sich ausgeben.
|
||||
<br />
|
||||
<br />
|
||||
Ferner haben Sie ein Recht auf Berichtigung oder Löschung oder auf
|
||||
Einschränkung der Verarbeitung, soweit Ihnen dies gesetzlich zusteht.
|
||||
<br />
|
||||
<br />
|
||||
Schließlich haben Sie ein Widerspruchsrecht gegen die Verarbeitung im
|
||||
Rahmen der gesetzlichen Vorgaben.
|
||||
<br />
|
||||
<br />
|
||||
Ein Recht auf Datenübertragbarkeit besteht ebenfalls im Rahmen der
|
||||
datenschutz-rechtlichen Vorgaben.
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong> Löschung von Daten </strong>
|
||||
<br />
|
||||
Wir löschen personenbezogene Daten grundsätzlich dann, wenn kein
|
||||
Erfordernis für eine weitere Speicherung besteht. Ein Erfordernis kann
|
||||
insbesondere dann bestehen, wenn die Daten noch benötigt werden, um
|
||||
vertragliche Leistungen zur erfüllen, Gewährleistungs- und ggf.
|
||||
Garantieansprüche prüfen und gewähren oder abwehren zu können. Im
|
||||
Falle von gesetzlichen Aufbewahrungspflichten kommt eine Löschung erst
|
||||
nach Ablauf der jeweiligen Aufbewahrungspflicht in Betracht.
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong> Beschwerderecht bei einer Aufsichtsbehörde </strong>
|
||||
<br />
|
||||
Sie haben das Recht, sich über die Verarbeitung personenbezogenen
|
||||
Daten durch uns bei einer Aufsichtsbehörde für den Datenschutz zu
|
||||
beschweren.
|
||||
<Divider sx={dividerStyle} />
|
||||
<strong> Änderung dieser Datenschutzhinweise </strong>
|
||||
<br />
|
||||
Wir überarbeiten diese Datenschutzhinweise bei Änderungen an dieser
|
||||
Internetseite oder bei sonstigen Anlässen, die dies erforderlich
|
||||
machen. Die jeweils aktuelle Fassung finden sie stets auf der
|
||||
Internetseite.
|
||||
<Divider sx={dividerStyle} />
|
||||
<Box
|
||||
style={{
|
||||
textAlign: "center",
|
||||
width: "100%",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
Stand: 07/2021
|
||||
</Box>
|
||||
</Typography>
|
||||
</Box>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Datenschutz;
|
|
@ -0,0 +1,142 @@
|
|||
import { Box, Divider, Grid, SxProps, Theme, Typography } from "@mui/material";
|
||||
import TopBar from "../components/TopBar";
|
||||
import { Footer } from "../components/Footer";
|
||||
import { useEffect } from "react";
|
||||
|
||||
let dividerStyle: SxProps<Theme> = {
|
||||
my: "20px",
|
||||
width: "100%",
|
||||
background: (theme) => theme.palette.secondary.main,
|
||||
};
|
||||
|
||||
function Impressum() {
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<TopBar />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
px: "10vw",
|
||||
mt: "5vh",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: "800",
|
||||
mb: "5vh",
|
||||
fontSize: "3rem",
|
||||
}}
|
||||
>
|
||||
Impressum
|
||||
</Typography>
|
||||
|
||||
<Divider sx={dividerStyle} />
|
||||
|
||||
<Grid container sx={{ width: "100%", height: "auto" }} spacing={5}>
|
||||
<Grid item lg={4}>
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: "light",
|
||||
mb: "5vh",
|
||||
fontSize: "1rem",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<strong> RF Computer GmbH & Co. KG </strong>
|
||||
<br />
|
||||
<br />
|
||||
<strong> Ralf Fink </strong>
|
||||
<br />
|
||||
<br />
|
||||
Otto-Bergmeyer-Str. 7
|
||||
<br />
|
||||
48431 Rheine
|
||||
<br />
|
||||
<br />
|
||||
Tel. 05971-911 11-0
|
||||
<br />
|
||||
Fax 05971-911 1119
|
||||
<br />
|
||||
www.rf-computer.de
|
||||
<br />
|
||||
mail: info@rf-computer.de
|
||||
<br />
|
||||
<br />
|
||||
USt-Id. -NR. DE345175566
|
||||
<br />
|
||||
<br />
|
||||
Steuer-Nr. 311/5991/2680
|
||||
<br />
|
||||
<br />
|
||||
AG Steinfurt HRA 7718
|
||||
<br />
|
||||
<br />
|
||||
Verantwortlich für journalistisch-redaktionelle Inhalte i.S.d. §
|
||||
18 Abs. 2 MStV: Ralf Fink
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item lg={4}>
|
||||
<Typography>
|
||||
<strong>
|
||||
{" "}
|
||||
© Copyright 2015-2019, RF Computer Alle Rechte vorbehalten.
|
||||
</strong>
|
||||
<br />
|
||||
<br />
|
||||
Gewährleistung
|
||||
<br />
|
||||
RF Computer verwendet die erforderliche Sorgfalt, um die
|
||||
Informationen der Website aktuell und korrekt zu halten. Dennoch
|
||||
kann nicht ausgeschlossen werden, dass Irrtümer auftreten oder
|
||||
Informationen unrichtig wiedergegeben werden. Besucher sollten aus
|
||||
diesem Grund die Richtigkeit und Vollständigkeit der Informationen
|
||||
nicht annehmen, sondern direkt mit RF Computer in Kontakt treten
|
||||
und die Angaben prüfen lassen.
|
||||
<br />
|
||||
<br />
|
||||
RF Computer hat keine Möglichkeit, zu kontrollieren, wie die
|
||||
Inhalte der Website verwendet oder beim Besucher angezeigt werden.
|
||||
RF Computer kann daher keine Haftung für unmittelbare und
|
||||
mittelbare Schäden, die aus diesen Informationen und / oder ihrer
|
||||
Verwendung entstehen, übernehmen.
|
||||
<br />
|
||||
<br />
|
||||
RF Computer kann nicht alle Webseiten, die mit der eigenen Website
|
||||
verlinkt sind, auf Ihren Inhalt überprüfen. Eine Haftung für die
|
||||
Inhalte fremder Websites ist daher in jedem Fall ausgeschlossen.
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item lg={4}>
|
||||
<Typography>
|
||||
<strong>Hinweis für Verbraucher:</strong>
|
||||
<br />
|
||||
<br />
|
||||
Plattform der Europäischen Kommission zur Online-Streitbeilegung
|
||||
(OS) für Verbraucher: https://ec.europa.eu/consumers/odr Wir sind
|
||||
nicht bereit und nicht verpflichtet, an einem
|
||||
Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle
|
||||
teilzunehmen.
|
||||
<br />
|
||||
<br />
|
||||
<strong>Urheberrecht</strong>
|
||||
<br />
|
||||
<br />
|
||||
RF Computer erteilt die Erlaubnis, alle auf der Website
|
||||
veröffentlichten Inhalte zur Informationsgewinnung zu nutzen, zu
|
||||
kopieren oder einen Ausdruck zu erstellen, sofern ein
|
||||
entsprechender Copyrighthinweis auf RF Computer erfolgt.
|
||||
Keinesfalls dürfen diese Inhalte jedoch verändert, zu
|
||||
Geschäftszwecken oder auf anderen Websites verwendet werden.
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Impressum;
|
|
@ -0,0 +1,167 @@
|
|||
import { Typography, Button, Box } from "@mui/material";
|
||||
import { DescriptionOutlined, BadgeOutlined } from "@mui/icons-material";
|
||||
import { AboutSection } from "../components/AboutSection";
|
||||
import { AboutSection2 } from "../components/AboutSection2";
|
||||
import { Footer } from "../components/Footer";
|
||||
import TopBar from "../components/TopBar";
|
||||
import { useTheme } from "@mui/material";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useEffect } from "react";
|
||||
|
||||
|
||||
function LandingPage() {
|
||||
const theme = useTheme();
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const element = document.getElementById(location.hash.replaceAll("#", ""));
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
}, [location]);
|
||||
return (
|
||||
<>
|
||||
<TopBar />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "calc(100vh - 100px)",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<video
|
||||
src="/assets/backgroundvid1.webm"
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
style={{
|
||||
height: "100vh",
|
||||
width: "100vw",
|
||||
position: "absolute",
|
||||
top: "50px",
|
||||
zIndex: -1,
|
||||
filter: "brightness(0.25)",
|
||||
objectFit: "cover",
|
||||
minWidth: "100vw",
|
||||
minHeight: "100vh",
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#fff",
|
||||
fontWeight: "bolder",
|
||||
fontSize: "7rem",
|
||||
letterSpacing: "0.05rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "center",
|
||||
|
||||
"@media (max-width: 1200px)": {
|
||||
fontSize: "4rem",
|
||||
},
|
||||
}}
|
||||
>
|
||||
von Rheine für Rheine
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#fff",
|
||||
fontWeight: "light",
|
||||
fontFamily: "Overpass Variable",
|
||||
fontSize: "1.25rem",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
Als regionale Unternehmen unterstützen wir regionale Vorhaben.
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
startIcon={<DescriptionOutlined />}
|
||||
sx={{
|
||||
color: "#fff",
|
||||
marginTop: "1rem",
|
||||
|
||||
"&:hover": {
|
||||
transform: "scale(1.1)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Download Flyer
|
||||
</Button>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "200px",
|
||||
background: `linear-gradient(45deg, #2f5c01, ${theme.palette.primary.main})`,
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#fff",
|
||||
fontWeight: "bolder",
|
||||
fontSize: "2rem",
|
||||
letterSpacing: "0.05rem",
|
||||
lineHeight: "1.2",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
Wir sind dabei !
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#fff",
|
||||
fontWeight: "light",
|
||||
fontFamily: "sans-serif",
|
||||
fontSize: "1rem",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
Danke an die bisherigen Teilnehmer !
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
startIcon={<BadgeOutlined />}
|
||||
sx={{
|
||||
color: "#fff",
|
||||
marginTop: "1rem",
|
||||
|
||||
"&:hover": {
|
||||
transform: "scale(1.1)",
|
||||
},
|
||||
}}
|
||||
href="#sponsors"
|
||||
>
|
||||
Sponsor Werden!
|
||||
</Button>
|
||||
</Box>
|
||||
<AboutSection />
|
||||
<AboutSection2 />
|
||||
<Sponsors />
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default LandingPage;
|
||||
|
||||
function Sponsors(): JSX.Element {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
</Box>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
import {
|
||||
LockOutlined,
|
||||
PersonOutlineOutlined,
|
||||
ArrowForwardOutlined,
|
||||
} from "@mui/icons-material";
|
||||
import {
|
||||
Alert,
|
||||
Box,
|
||||
Button,
|
||||
CircularProgress,
|
||||
Collapse,
|
||||
InputAdornment,
|
||||
TextField,
|
||||
} from "@mui/material";
|
||||
import axios, { AxiosError } from "axios";
|
||||
import { sha256 } from "js-sha256";
|
||||
import { useEffect, useState } from "react";
|
||||
import { getBaseURL } from "../functions";
|
||||
|
||||
let backgrounds = ["/assets/background1.jpg", "/assets/background2.jpg"];
|
||||
|
||||
function Login() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<false | string>(false);
|
||||
const [currentBackground, setCurrentBackground] = useState(0);
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
const valid = () => {
|
||||
if (username.length < 3) return false;
|
||||
if (password.length < 3) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const login = () => {
|
||||
setLoading(true);
|
||||
axios
|
||||
.post(`${getBaseURL()}/api/auth/login`, {
|
||||
username: username.trim(),
|
||||
password: sha256(`rheine ${password.trim()} rheine`),
|
||||
})
|
||||
.then((res) => {
|
||||
localStorage.setItem("token", res.data.token);
|
||||
setTimeout(() => (window.location.href = "/admin/dashboard"), 1000);
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
setLoading(false);
|
||||
if ((err.response?.data as any).message) {
|
||||
setError((err.response?.data as any).message);
|
||||
} else {
|
||||
setError(err.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setCurrentBackground((current) => (current + 1) % backgrounds.length);
|
||||
}, 15000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (localStorage.getItem("token")) {
|
||||
axios
|
||||
.get(`${getBaseURL()}/api/auth/verify`, {
|
||||
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
|
||||
})
|
||||
.then((res) => {
|
||||
window.location.href = "/admin/dashboard";
|
||||
})
|
||||
.catch((err) => {
|
||||
localStorage.removeItem("token");
|
||||
setLoading(false);
|
||||
});
|
||||
} else {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
background: `linear-gradient(45deg, #0022ff55, #00AA0099)`,
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundAttachment: "fixed",
|
||||
objectFit: "cover",
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
{backgrounds.map((background, index) => (
|
||||
<img
|
||||
src={background}
|
||||
alt=""
|
||||
key={index}
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
objectFit: "cover",
|
||||
zIndex: -1,
|
||||
opacity: currentBackground === index ? 1 : 0,
|
||||
transition: "opacity 5s ease-in-out",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<Box
|
||||
sx={{
|
||||
width: "500px",
|
||||
height: "500px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
|
||||
backgroundColor: "#fff",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/logo.png"
|
||||
alt=""
|
||||
width="250px"
|
||||
style={{
|
||||
marginBottom: "30px",
|
||||
pointerEvents: "none",
|
||||
userSelect: "none",
|
||||
}}
|
||||
/>
|
||||
|
||||
<Collapse in={error !== false}>
|
||||
<Alert severity="error" sx={{ width: "300px" }}>
|
||||
{error}
|
||||
</Alert>
|
||||
</Collapse>
|
||||
|
||||
<TextField
|
||||
required
|
||||
variant="outlined"
|
||||
label="Benutzername"
|
||||
type="username"
|
||||
value={username}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") login();
|
||||
}}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<PersonOutlineOutlined />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
sx={{
|
||||
width: "300px",
|
||||
height: "50px",
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
variant="outlined"
|
||||
label="Passwort"
|
||||
type="password"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") login();
|
||||
}}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<LockOutlined />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
sx={{
|
||||
width: "300px",
|
||||
height: "50px",
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
endIcon={
|
||||
<ArrowForwardOutlined
|
||||
sx={{
|
||||
display: loading ? "none" : "block",
|
||||
}}
|
||||
/>
|
||||
}
|
||||
onClick={login}
|
||||
disabled={loading || !valid()}
|
||||
sx={{
|
||||
mt: "20px",
|
||||
width: "100px",
|
||||
gap: "0px",
|
||||
backgroundColor: (theme) => theme.palette.primary.main,
|
||||
|
||||
transition: "all 0.3s ease-in-out",
|
||||
"&:hover": {
|
||||
gap: "10px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{loading ? <CircularProgress size={20} /> : "Login"}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Login;
|
|
@ -0,0 +1,381 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import SunEditor from "suneditor-react";
|
||||
import SunEditorCore from "suneditor/src/lib/core";
|
||||
import "suneditor/dist/css/suneditor.min.css"; // Import Sun Editor's CSS File
|
||||
import {
|
||||
fontSize,
|
||||
formatBlock,
|
||||
image,
|
||||
video,
|
||||
table,
|
||||
list,
|
||||
align,
|
||||
} from "suneditor/src/plugins";
|
||||
import {
|
||||
Box,
|
||||
CircularProgress,
|
||||
Typography,
|
||||
Grid,
|
||||
Backdrop,
|
||||
} from "@mui/material";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { AddBox } from "@mui/icons-material";
|
||||
import { getBaseURL } from "../../functions";
|
||||
import axios from "axios";
|
||||
|
||||
function ArticleEditor() {
|
||||
const navigate = useNavigate();
|
||||
const editor = useRef<SunEditorCore>();
|
||||
|
||||
// The sunEditor parameter will be set to the core suneditor instance when this function is called
|
||||
const getSunEditorInstance = (sunEditor: SunEditorCore) => {
|
||||
editor.current = sunEditor;
|
||||
sunEditor.save();
|
||||
};
|
||||
|
||||
let { id } = useParams() as {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const upLoadInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
const [banner, setBanner] = useState<string>("");
|
||||
|
||||
const loadbanner = () => {
|
||||
axios
|
||||
.get(`${getBaseURL()}/api/article/banner/${id}`)
|
||||
.then((response) => {
|
||||
setBanner(response.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
setBanner("https://placehold.co/1920x1080");
|
||||
});
|
||||
};
|
||||
|
||||
const save = async (content: string) => {
|
||||
const title = document.getElementById("article-title")?.innerText;
|
||||
const banner = document.getElementById("articleBanner-content")?.getAttribute("src");
|
||||
if (!title) return false;
|
||||
if (!banner) return false;
|
||||
|
||||
console.log(content);
|
||||
setSaving(true);
|
||||
const res = await axios
|
||||
.post(
|
||||
!id
|
||||
? `${getBaseURL()}/api/article/create`
|
||||
: `${getBaseURL()}/api/article/edit/${id}`,
|
||||
{
|
||||
title: title,
|
||||
content: content,
|
||||
image: banner,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
setSaving(false);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (!res) return;
|
||||
|
||||
navigate(`/admin/editor/${res.data.ID}`);
|
||||
id = res.data.ID;
|
||||
|
||||
setSaving(false);
|
||||
return true;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
setLoading(true);
|
||||
loadbanner();
|
||||
|
||||
axios.get(`${getBaseURL()}/api/article/view/${id}`).then((res) => {
|
||||
if (!res.data) return;
|
||||
|
||||
document.getElementById("article-title")!.innerText = res.data.title;
|
||||
|
||||
axios.get(`${getBaseURL()}/api/article/content/${id}`).then((res) => {
|
||||
if (!res.data) return;
|
||||
|
||||
editor.current?.setContents(res.data);
|
||||
|
||||
setTimeout(() => {
|
||||
// get all images with a data-size property
|
||||
const images = document.querySelectorAll("img[data-size]");
|
||||
console.log(images);
|
||||
|
||||
// loop through all images and set the width style to the data-size property
|
||||
for (let image of [...images]) {
|
||||
(image as HTMLElement).style.width =
|
||||
image.getAttribute("data-size")?.replaceAll(",", "") ?? "auto";
|
||||
}
|
||||
setLoading(false);
|
||||
}, 500);
|
||||
});
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading && <CircularProgress />}
|
||||
{!loading && (
|
||||
<Box
|
||||
className="articleBanner"
|
||||
sx={{
|
||||
height: "100vh",
|
||||
width: "100vw",
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
left: 0,
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-start",
|
||||
zIndex: 0,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
upLoadInputRef.current?.click();
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={banner}
|
||||
id="articleBanner-content"
|
||||
alt=""
|
||||
style={{
|
||||
filter: "brightness(0.5)",
|
||||
height: "100vh",
|
||||
transform: "translateX(125px)",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<input
|
||||
ref={upLoadInputRef}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
style={{ display: "none" }}
|
||||
onChange={(e) => {
|
||||
const file = e.target.files?.[0]
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
setBanner(reader.result as string);
|
||||
};
|
||||
}}
|
||||
onAbort={() => {}}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
<Box sx={{
|
||||
zIndex: 3,
|
||||
}}>
|
||||
<Backdrop
|
||||
open={saving}
|
||||
sx={{
|
||||
zIndex: 9999,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
backgroundColor: "#fff",
|
||||
padding: "20px",
|
||||
}}
|
||||
>
|
||||
<CircularProgress />
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "20px",
|
||||
fontWeight: 700,
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
Wird Gespeichert...
|
||||
</Typography>
|
||||
</Box>
|
||||
</Backdrop>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
|
||||
px: "15%",
|
||||
|
||||
"@media screen and (max-width: 768px)": {
|
||||
px: "0px",
|
||||
},
|
||||
|
||||
"@media screen and (max-width: 1300px)": {
|
||||
px: "2px",
|
||||
},
|
||||
|
||||
"@media screen and (min-width: 1800px)": {
|
||||
px: "15%",
|
||||
},
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
|
||||
transform: "translateY(25vh)",
|
||||
zIndex: 3,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "1080px",
|
||||
height: "fit-content",
|
||||
boxShadow: "0px 0px 10px 0px rgba(0,0,0,0.5)",
|
||||
opacity: loading ? 0 : 1,
|
||||
padding: "40px",
|
||||
backgroundColor: "white",
|
||||
zIndex: 3,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h1"
|
||||
contentEditable
|
||||
id="article-title"
|
||||
sx={{
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "3rem",
|
||||
fontWeight: 700,
|
||||
textAlign: "left",
|
||||
mb: "20px",
|
||||
}}
|
||||
>
|
||||
Lorem ipsum whatever
|
||||
</Typography>
|
||||
|
||||
<Grid
|
||||
container
|
||||
spacing={2}
|
||||
sx={{
|
||||
pb: "20px",
|
||||
}}
|
||||
>
|
||||
<SponsorImageSmall
|
||||
name="Felix"
|
||||
image="/logo.png"
|
||||
description="Felix ist ein cooler Typ"
|
||||
link="#"
|
||||
/>
|
||||
<SponsorImageSmall
|
||||
name="Felix"
|
||||
image="/AdvanTex.jpg"
|
||||
description="Felix ist ein cooler Typ"
|
||||
link="https://advantex.de/"
|
||||
/>
|
||||
|
||||
<Grid item>
|
||||
<AddBox
|
||||
fontSize="large"
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
|
||||
transition: "all 0.2s ease-in-out",
|
||||
"&:hover": {
|
||||
color: "#000000AA",
|
||||
transform: "scale(1.1)",
|
||||
},
|
||||
}}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<SunEditor
|
||||
lang="de"
|
||||
setDefaultStyle="font-family: 'Lexend Variable';"
|
||||
getSunEditorInstance={getSunEditorInstance}
|
||||
onSave={save}
|
||||
setOptions={{
|
||||
plugins: [
|
||||
image,
|
||||
fontSize,
|
||||
formatBlock,
|
||||
video,
|
||||
table,
|
||||
list,
|
||||
align,
|
||||
],
|
||||
font: ["Lexend Variable"],
|
||||
defaultStyle: "font-family: 'Lexend Variable';",
|
||||
stickyToolbar: "0",
|
||||
buttonList: [
|
||||
["save"],
|
||||
["undo", "redo"],
|
||||
["fontSize", "formatBlock"],
|
||||
[
|
||||
"bold",
|
||||
"underline",
|
||||
"italic",
|
||||
"strike",
|
||||
"subscript",
|
||||
"superscript",
|
||||
],
|
||||
["removeFormat"],
|
||||
["outdent", "indent"],
|
||||
["align", "list", "table"],
|
||||
["image", "video"],
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default ArticleEditor;
|
||||
|
||||
function SponsorImageSmall({
|
||||
name,
|
||||
image,
|
||||
description,
|
||||
link,
|
||||
}: {
|
||||
name: string;
|
||||
image: string;
|
||||
description: string;
|
||||
link: string;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<Grid item>
|
||||
<Box sx={{}}>
|
||||
<img
|
||||
src={image}
|
||||
alt=""
|
||||
style={{
|
||||
height: "25px",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,245 @@
|
|||
import { Add, Delete, Edit } from "@mui/icons-material";
|
||||
import {
|
||||
Backdrop,
|
||||
Box,
|
||||
Button,
|
||||
CircularProgress,
|
||||
Divider,
|
||||
IconButton,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import axios from "axios";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { formatNum, getBaseURL } from "../../functions";
|
||||
import moment from "moment";
|
||||
|
||||
function Artikel() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [articles, setArticles] = useState<Types.Article[]>([]);
|
||||
const [loaded, setLoaded] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
setLoaded(false);
|
||||
|
||||
axios
|
||||
.get(`${getBaseURL()}/api/allArticles`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
setArticles(res.data);
|
||||
setLoaded(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
|
||||
pt: "30px",
|
||||
px: "30px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "40px",
|
||||
fontWeight: "bold",
|
||||
ml: "20px",
|
||||
}}
|
||||
>
|
||||
Artikel
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{}}
|
||||
onClick={() => {
|
||||
navigate("/admin/editor");
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
<Add /> Neu{" "}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Divider
|
||||
sx={{
|
||||
width: "100%",
|
||||
backgroundColor: "#000000AA",
|
||||
}}
|
||||
/>
|
||||
|
||||
<Table
|
||||
sx={{
|
||||
width: "100%",
|
||||
mt: "30px",
|
||||
}}
|
||||
>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell align="left" width="700px">
|
||||
Titel
|
||||
</TableCell>
|
||||
<TableCell align="right">Autor</TableCell>
|
||||
<TableCell align="right">Aufrufe</TableCell>
|
||||
<TableCell align="right">Datum</TableCell>
|
||||
<TableCell align="right">Optionen</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
<Backdrop
|
||||
open={!loaded}
|
||||
sx={{
|
||||
zIndex: (theme) => theme.zIndex.drawer + 1,
|
||||
color: "#fff",
|
||||
}}
|
||||
>
|
||||
<CircularProgress
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
/>
|
||||
</Backdrop>
|
||||
{loaded &&
|
||||
articles.map((article) => (
|
||||
<TableRow>
|
||||
<TableCell align="left">
|
||||
<Link
|
||||
target="_blank"
|
||||
to={`/artikel/${article.ID}`}>
|
||||
{article.title}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell align="right">{article.author.username}</TableCell>
|
||||
<TableCell align="right">{formatNum(article.views)}</TableCell>
|
||||
<TableCell align="right" width="200px" sx={{}}>
|
||||
<Tooltip
|
||||
arrow
|
||||
placement="top"
|
||||
title={moment(article.createdAt).locale("de").fromNow()}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
width: "fit-content",
|
||||
marginLeft: "auto",
|
||||
}}
|
||||
>
|
||||
{moment(article.createdAt).locale("de").format("ll")}
|
||||
</p>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell align="right" width="60px">
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
sx={{
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
|
||||
padding: "5px",
|
||||
backgroundColor: "#fff",
|
||||
borderRadius: "5px",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate(`/admin/editor/${article.ID}`);
|
||||
}}
|
||||
>
|
||||
|
||||
<Edit
|
||||
sx={{
|
||||
fontSize: "20px",
|
||||
color: "#000000CC",
|
||||
}}
|
||||
|
||||
/>
|
||||
|
||||
</IconButton>
|
||||
<IconButton
|
||||
sx={{
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
|
||||
padding: "5px",
|
||||
backgroundColor: "#fff",
|
||||
borderRadius: "5px",
|
||||
"&:hover": {
|
||||
background: "red",
|
||||
},
|
||||
}}
|
||||
onClick={() => {
|
||||
setLoaded(false);
|
||||
axios
|
||||
.delete(`${getBaseURL()}/api/article/${article.ID}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem(
|
||||
"token"
|
||||
)}`,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
setArticles(
|
||||
articles.filter(
|
||||
(e) => e.ID !== article.ID
|
||||
)
|
||||
);
|
||||
setLoaded(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Delete
|
||||
sx={{
|
||||
fontSize: "20px",
|
||||
color: "#000000CC",
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Artikel;
|
|
@ -0,0 +1,181 @@
|
|||
import {
|
||||
ManageAccounts,
|
||||
PersonAdd,
|
||||
PersonRemove,
|
||||
} from "@mui/icons-material";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Divider,
|
||||
IconButton,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import axios from "axios";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { getBaseURL } from "../../functions";
|
||||
|
||||
function Benutzer() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [users, setUsers] = useState<Types.User[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
axios
|
||||
.get(`${getBaseURL()}/api/users/all`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
setUsers(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
|
||||
pt: "30px",
|
||||
px: "30px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "40px",
|
||||
fontWeight: "bold",
|
||||
ml: "20px",
|
||||
}}
|
||||
>
|
||||
Benutzer
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate("/admin/sponsor/");
|
||||
}}
|
||||
>
|
||||
<PersonAdd /> Neue Benutzer
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Divider
|
||||
sx={{
|
||||
width: "100%",
|
||||
backgroundColor: "#000000AA",
|
||||
}}
|
||||
/>
|
||||
|
||||
<Table
|
||||
sx={{
|
||||
width: "100%",
|
||||
mt: "30px",
|
||||
}}
|
||||
>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell align="left" width="700px">
|
||||
Name
|
||||
</TableCell>
|
||||
<TableCell align="right">Berechtigungen</TableCell>
|
||||
<TableCell align="right">Optionen</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{users.map((user) => (
|
||||
<TableRow>
|
||||
<TableCell align="left">{user.username}</TableCell>
|
||||
<TableCell align="right">
|
||||
{[
|
||||
{ name: "Admin", value: user.admin },
|
||||
{ name: "Artikel Erstellen", value: user.article_create },
|
||||
{ name: "Artikel Verwalten", value: user.article_manage },
|
||||
{ name: "Sponsoren Verwalten", value: user.sponsor_manage },
|
||||
{ name: "Nutzer Verwalten", value: user.user_manage },
|
||||
]
|
||||
.filter((e) => e.value)
|
||||
.map((e) => e.name)
|
||||
.join(", ")}
|
||||
</TableCell>
|
||||
<TableCell align="right" width="200px"></TableCell>
|
||||
<TableCell align="right" width="60px">
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
sx={{
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
|
||||
padding: "5px",
|
||||
backgroundColor: "#fff",
|
||||
borderRadius: "5px",
|
||||
}}
|
||||
>
|
||||
<ManageAccounts />
|
||||
</IconButton>
|
||||
|
||||
<IconButton
|
||||
sx={{
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
|
||||
padding: "5px",
|
||||
backgroundColor: "#fff",
|
||||
borderRadius: "5px",
|
||||
"&:hover": {
|
||||
background: "red",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<PersonRemove />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Benutzer;
|
|
@ -0,0 +1,110 @@
|
|||
import { Visibility, Newspaper, Person, Savings } from "@mui/icons-material";
|
||||
import { Box, Grid, SvgIconTypeMap, Typography } from "@mui/material";
|
||||
import { OverridableComponent } from "@mui/material/OverridableComponent";
|
||||
|
||||
function Dashboard() {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
|
||||
pt: "30px",
|
||||
px: "30px",
|
||||
}}
|
||||
>
|
||||
<Grid container spacing={1}>
|
||||
<StatDisplay Title="Benutzer" Icon={Person} Value="0" />
|
||||
<StatDisplay Title="Artikel" Icon={Newspaper} Value="0" />
|
||||
<StatDisplay Title="Sponsoren" Icon={Savings} Value="0" />
|
||||
<StatDisplay Title="Klicks" Icon={Visibility} Value="0" />
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Dashboard;
|
||||
|
||||
function StatDisplay({
|
||||
Title,
|
||||
Icon,
|
||||
Value,
|
||||
}: {
|
||||
Title: string;
|
||||
Icon: OverridableComponent<SvgIconTypeMap<{}, "svg">> & {
|
||||
muiName: string;
|
||||
};
|
||||
Value: string;
|
||||
}) {
|
||||
return (
|
||||
<Grid item lg={3} md={6} sm={12}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
||||
padding: "10px",
|
||||
background: (theme) => `linear-gradient(45deg, ${theme.palette.secondary.light}, ${theme.palette.secondary.main})`,
|
||||
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "24px",
|
||||
color: "#000000AA",
|
||||
}}
|
||||
>
|
||||
{Title}
|
||||
</Typography>
|
||||
|
||||
<Box sx={{
|
||||
width: "50px",
|
||||
height: "50px",
|
||||
|
||||
padding: "5px",
|
||||
backgroundColor: "#fff",
|
||||
borderRadius: "5px",
|
||||
}}>
|
||||
<Icon
|
||||
sx={{
|
||||
fontSize: "40px",
|
||||
color: "#000000CC",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
}}>
|
||||
<Typography sx={{
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "40px",
|
||||
color: "#000000FF",
|
||||
}}>
|
||||
{Value}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
import {
|
||||
ManageAccounts,
|
||||
DomainDisabledRounded,
|
||||
DomainAddRounded,
|
||||
} from "@mui/icons-material";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Divider,
|
||||
IconButton,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Typography,
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import SponsorModal from "../../components/SponsorModal";
|
||||
import { useState } from "react";
|
||||
|
||||
function Sponsoren() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [modalID, setModelID] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SponsorModal id={modalID} onClose={() => {
|
||||
setModelID(null);
|
||||
}} />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
|
||||
pt: "30px",
|
||||
px: "30px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Lexend Variable",
|
||||
fontSize: "40px",
|
||||
fontWeight: "bold",
|
||||
ml: "20px",
|
||||
}}
|
||||
>
|
||||
Sponsoren
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
}}
|
||||
onClick={() => {
|
||||
setModelID("create");
|
||||
}}
|
||||
>
|
||||
<DomainAddRounded /> Neue Sponsoren
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Divider
|
||||
sx={{
|
||||
width: "100%",
|
||||
backgroundColor: "#000000AA",
|
||||
}}
|
||||
/>
|
||||
|
||||
<Table
|
||||
sx={{
|
||||
width: "100%",
|
||||
mt: "30px",
|
||||
}}
|
||||
>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell align="left" width="700px">
|
||||
Name
|
||||
</TableCell>
|
||||
<TableCell align="right">Ansprechpartner</TableCell>
|
||||
<TableCell align="right">Datum</TableCell>
|
||||
<TableCell align="right">Optionen</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell align="left">RF Computer GMBH</TableCell>
|
||||
<TableCell align="right">R. Fink</TableCell>
|
||||
<TableCell align="right" width="200px">
|
||||
7 Nov. 2023
|
||||
</TableCell>
|
||||
<TableCell align="right" width="60px">
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Tooltip title="Daten anpassen">
|
||||
<IconButton
|
||||
sx={{
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
|
||||
padding: "5px",
|
||||
backgroundColor: "#fff",
|
||||
borderRadius: "5px",
|
||||
}}
|
||||
>
|
||||
<ManageAccounts />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="Sponsor löschen">
|
||||
<IconButton
|
||||
sx={{
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
|
||||
padding: "5px",
|
||||
backgroundColor: "#fff",
|
||||
borderRadius: "5px",
|
||||
"&:hover": {
|
||||
background: "red",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DomainDisabledRounded />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Sponsoren;
|
|
@ -0,0 +1,28 @@
|
|||
declare namespace Types {
|
||||
interface Article {
|
||||
ID: string;
|
||||
title: string;
|
||||
views: number;
|
||||
author: {
|
||||
ID: string;
|
||||
username: string;
|
||||
}
|
||||
sponsors: {
|
||||
ID: string;
|
||||
name: string;
|
||||
url: string;
|
||||
description: string;
|
||||
}[];
|
||||
updatedAt: Date;
|
||||
createdAt: Date;
|
||||
}
|
||||
interface User {
|
||||
ID: string;
|
||||
username: string;
|
||||
admin: boolean;
|
||||
article_create: boolean;
|
||||
article_manage: boolean;
|
||||
sponsor_manage: boolean;
|
||||
user_manage: boolean;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue