Update logo, .gitignore, functions, index, and index.html files
|
@ -2,3 +2,4 @@ node_modules
|
||||||
dist
|
dist
|
||||||
# Keep environment variables out of version control
|
# Keep environment variables out of version control
|
||||||
.env
|
.env
|
||||||
|
build
|
|
@ -9,43 +9,17 @@ datasource db {
|
||||||
extensions = [uuid_ossp(map: "uuid-ossp")]
|
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 {
|
model users {
|
||||||
ID String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
|
ID String @id @default(dbgenerated("uuid_generate_v1()")) @db.Uuid
|
||||||
username String @db.VarChar(64)
|
username String
|
||||||
password String @db.VarChar(64)
|
password String @db.VarChar(128)
|
||||||
token String @db.VarChar(128)
|
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 {
|
model Mitarbeiter {
|
||||||
ID String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
|
ID String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
|
||||||
name String @db.VarChar(12)
|
Vorname String
|
||||||
url String @db.VarChar(128)
|
Nachname String
|
||||||
description String @db.VarChar(100)
|
Anstelldatum DateTime @db.Date
|
||||||
|
Geburtstag DateTime @db.Date
|
||||||
addedAt DateTime @default(now())
|
|
||||||
|
|
||||||
articles articles[]
|
|
||||||
}
|
}
|
||||||
|
|
After Width: | Height: | Size: 36 KiB |
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"files": {
|
||||||
|
"main.css": "/static/css/main.3145608a.css",
|
||||||
|
"main.js": "/static/js/main.20c1133d.js",
|
||||||
|
"static/media/lexend-latin-wght-normal.woff2": "/static/media/lexend-latin-wght-normal.f0861a122355d47a9b27.woff2",
|
||||||
|
"static/media/overpass-latin-wght-normal.woff2": "/static/media/overpass-latin-wght-normal.4b83544cf101ef6f5e10.woff2",
|
||||||
|
"static/media/overpass-latin-ext-wght-normal.woff2": "/static/media/overpass-latin-ext-wght-normal.c7886930b5698c36c8d2.woff2",
|
||||||
|
"static/media/lexend-latin-ext-wght-normal.woff2": "/static/media/lexend-latin-ext-wght-normal.94b395946a6799fff8c8.woff2",
|
||||||
|
"static/media/overpass-cyrillic-wght-normal.woff2": "/static/media/overpass-cyrillic-wght-normal.5b31761fef8ac8222a1b.woff2",
|
||||||
|
"static/media/overpass-cyrillic-ext-wght-normal.woff2": "/static/media/overpass-cyrillic-ext-wght-normal.08d0aad8582d63a917c6.woff2",
|
||||||
|
"static/media/lexend-vietnamese-wght-normal.woff2": "/static/media/lexend-vietnamese-wght-normal.ac5494e335c7c2cb000d.woff2",
|
||||||
|
"static/media/overpass-vietnamese-wght-normal.woff2": "/static/media/overpass-vietnamese-wght-normal.d1e91653b90562a59206.woff2",
|
||||||
|
"index.html": "/index.html",
|
||||||
|
"main.3145608a.css.map": "/static/css/main.3145608a.css.map",
|
||||||
|
"main.20c1133d.js.map": "/static/js/main.20c1133d.js.map"
|
||||||
|
},
|
||||||
|
"entrypoints": [
|
||||||
|
"static/css/main.3145608a.css",
|
||||||
|
"static/js/main.20c1133d.js"
|
||||||
|
]
|
||||||
|
}
|
After Width: | Height: | Size: 148 KiB |
After Width: | Height: | Size: 224 KiB |
After Width: | Height: | Size: 5.9 MiB |
After Width: | Height: | Size: 192 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/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="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>DWL Birthdays</title><script defer="defer" src="/static/js/main.20c1133d.js"></script><link href="/static/css/main.3145608a.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
After Width: | Height: | Size: 149 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,2 @@
|
||||||
|
body{margin:0;overflow-x:hidden;padding:0}body,label,span{font-family:Arial,Helvetica,sans-serif}@-webkit-keyframes gradientAnim{0%{background-position:0 50%}50%{background-position:100% 50%}to{background-position:0 50%}}@keyframes gradientAnim{0%{background-position:0 50%}50%{background-position:100% 50%}to{background-position:0 50%}}.nav-link{color:#005487;font-size:25px;font-weight:700;letter-spacing:.05em;line-height:24px;overflow:hidden;text-align:center;text-decoration:none;text-overflow:ellipsis;transition:all .3s ease 0s;white-space:nowrap;width:auto!important}.nav-link:after{background:#005487;content:"";display:block;height:2px;margin-left:auto;margin-right:auto;transition:width .3s ease 0s;width:0}.nav-link:hover:after{width:100%}.nav-link-left{color:#005487;font-size:25px;font-weight:700;letter-spacing:.05em;line-height:24px;overflow:hidden;text-align:center;text-decoration:none;text-overflow:ellipsis;transition:all .3s ease 0s;white-space:nowrap;width:auto!important}.nav-link-left:after{background:#fff;content:"";display:block;height:2px;margin-right:auto;transition:width .3s ease 0s;width:0}.nav-link-left:hover:after{width:100%}.se-resizing-bar{display:none!important}.sun-editor-editable{height:auto!important}.se-toolbar{position:-webkit-sticky!important;position:sticky!important;-webkit-transform:translate(-105px);transform:translate(-105px);width:100px!important;z-index:0!important}.se-btn-tray,.se-toolbar{flex-direction:column!important}.se-btn-tray{align-items:flex-start!important;display:flex!important;gap:2px!important}.se-wrapper{margin-top:-480px}.articleBanner{transition:.3s ease}.articleBanner:hover{-webkit-filter:brightness(.7);filter:brightness(.7)}a{color:#000;text-decoration:none}input[name=suneditor_image_radio]{display:none}@font-face{font-display:swap;font-family:Overpass Variable;font-style:normal;font-weight:100 900;src:url(/static/media/overpass-cyrillic-ext-wght-normal.08d0aad8582d63a917c6.woff2) format("woff2-variations");unicode-range:u+0460-052f,u+1c80-1c88,u+20b4,u+2de0-2dff,u+a640-a69f,u+fe2e-fe2f}@font-face{font-display:swap;font-family:Overpass Variable;font-style:normal;font-weight:100 900;src:url(/static/media/overpass-cyrillic-wght-normal.5b31761fef8ac8222a1b.woff2) format("woff2-variations");unicode-range:u+0301,u+0400-045f,u+0490-0491,u+04b0-04b1,u+2116}@font-face{font-display:swap;font-family:Overpass Variable;font-style:normal;font-weight:100 900;src:url(/static/media/overpass-vietnamese-wght-normal.d1e91653b90562a59206.woff2) format("woff2-variations");unicode-range:u+0102-0103,u+0110-0111,u+0128-0129,u+0168-0169,u+01a0-01a1,u+01af-01b0,u+0300-0301,u+0303-0304,u+0308-0309,u+0323,u+0329,u+1ea0-1ef9,u+20ab}@font-face{font-display:swap;font-family:Overpass Variable;font-style:normal;font-weight:100 900;src:url(/static/media/overpass-latin-ext-wght-normal.c7886930b5698c36c8d2.woff2) format("woff2-variations");unicode-range:u+0100-02af,u+0304,u+0308,u+0329,u+1e00-1e9f,u+1ef2-1eff,u+2020,u+20a0-20ab,u+20ad-20cf,u+2113,u+2c60-2c7f,u+a720-a7ff}@font-face{font-display:swap;font-family:Overpass Variable;font-style:normal;font-weight:100 900;src:url(/static/media/overpass-latin-wght-normal.4b83544cf101ef6f5e10.woff2) format("woff2-variations");unicode-range:u+00??,u+0131,u+0152-0153,u+02bb-02bc,u+02c6,u+02da,u+02dc,u+0304,u+0308,u+0329,u+2000-206f,u+2074,u+20ac,u+2122,u+2191,u+2193,u+2212,u+2215,u+feff,u+fffd}@font-face{font-display:swap;font-family:Lexend Variable;font-style:normal;font-weight:100 900;src:url(/static/media/lexend-vietnamese-wght-normal.ac5494e335c7c2cb000d.woff2) format("woff2-variations");unicode-range:u+0102-0103,u+0110-0111,u+0128-0129,u+0168-0169,u+01a0-01a1,u+01af-01b0,u+0300-0301,u+0303-0304,u+0308-0309,u+0323,u+0329,u+1ea0-1ef9,u+20ab}@font-face{font-display:swap;font-family:Lexend Variable;font-style:normal;font-weight:100 900;src:url(/static/media/lexend-latin-ext-wght-normal.94b395946a6799fff8c8.woff2) format("woff2-variations");unicode-range:u+0100-02af,u+0304,u+0308,u+0329,u+1e00-1e9f,u+1ef2-1eff,u+2020,u+20a0-20ab,u+20ad-20cf,u+2113,u+2c60-2c7f,u+a720-a7ff}@font-face{font-display:swap;font-family:Lexend Variable;font-style:normal;font-weight:100 900;src:url(/static/media/lexend-latin-wght-normal.f0861a122355d47a9b27.woff2) format("woff2-variations");unicode-range:u+00??,u+0131,u+0152-0153,u+02bb-02bc,u+02c6,u+02da,u+02dc,u+0304,u+0308,u+0329,u+2000-206f,u+2074,u+20ac,u+2122,u+2191,u+2193,u+2212,u+2215,u+feff,u+fffd}
|
||||||
|
/*# sourceMappingURL=main.3145608a.css.map*/
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license React
|
||||||
|
* react-dom.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license React
|
||||||
|
* react-is.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license React
|
||||||
|
* react-jsx-runtime.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license React
|
||||||
|
* react.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license React
|
||||||
|
* scheduler.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @remix-run/router v1.9.0
|
||||||
|
*
|
||||||
|
* Copyright (c) Remix Software Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE.md file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React Router v6.16.0
|
||||||
|
*
|
||||||
|
* Copyright (c) Remix Software Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE.md file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [js-sha256]{@link https://github.com/emn178/js-sha256}
|
||||||
|
*
|
||||||
|
* @version 0.10.1
|
||||||
|
* @author Chen, Yi-Cyuan [emn178@gmail.com]
|
||||||
|
* @copyright Chen, Yi-Cyuan 2014-2023
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license React v16.13.1
|
||||||
|
* react-is.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//! moment.js
|
|
@ -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>
|
|
@ -11,8 +11,6 @@ dotenv.config()
|
||||||
const prisma = new PrismaClient()
|
const prisma = new PrismaClient()
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
fs.mkdirSync("/var/lib/rheinefuerrheine/artikel", { recursive: true })
|
|
||||||
fs.mkdirSync("/var/lib/rheinefuerrheine/sponsoren", { recursive: true })
|
|
||||||
|
|
||||||
app.use(express.json({
|
app.use(express.json({
|
||||||
limit: '50mb'
|
limit: '50mb'
|
||||||
|
@ -25,13 +23,11 @@ app.use((req, res, next) => {
|
||||||
next();
|
next();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.use(express.static('public'))
|
||||||
|
|
||||||
export { prisma, app }
|
export { prisma, app }
|
||||||
init()
|
init()
|
||||||
|
|
||||||
app.get('/', (req, res) => {
|
|
||||||
res.send("You took the wrong turn, buddy.")
|
|
||||||
})
|
|
||||||
|
|
||||||
app.post('/api/auth/login', async (req, res) => {
|
app.post('/api/auth/login', async (req, res) => {
|
||||||
const { username, password } = req.body
|
const { username, password } = req.body
|
||||||
|
|
||||||
|
@ -65,7 +61,7 @@ app.get('/api/auth/verify', async (req, res) => {
|
||||||
res.send('OK')
|
res.send('OK')
|
||||||
})
|
})
|
||||||
|
|
||||||
app.post("/api/article/create", async (req, res) => {
|
app.post("/api/worker/create", async (req, res) => {
|
||||||
const token = req.headers.authorization?.split(' ')[1]
|
const token = req.headers.authorization?.split(' ')[1]
|
||||||
|
|
||||||
if (!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
if (!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||||
|
@ -77,31 +73,9 @@ app.post("/api/article/create", async (req, res) => {
|
||||||
if (!title || !content || !image) return res.status(400).send(Errors.MISSING_ITEMS)
|
if (!title || !content || !image) return res.status(400).send(Errors.MISSING_ITEMS)
|
||||||
|
|
||||||
// sponsors is an array of sponsor IDs
|
// sponsors is an array of sponsor IDs
|
||||||
|
|
||||||
const article = await prisma.articles.create({
|
|
||||||
data: {
|
|
||||||
title,
|
|
||||||
public: true,
|
|
||||||
author: {
|
|
||||||
connect: {
|
|
||||||
ID: user.ID
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sponsors: {
|
|
||||||
connect: sponsors.map((sponsor: string) => ({
|
|
||||||
ID: sponsor
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
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) => {
|
app.get('/api/workers', async (req, res) => {
|
||||||
const token = req.headers.authorization?.split(' ')[1]
|
const token = req.headers.authorization?.split(' ')[1]
|
||||||
|
|
||||||
if (!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
if (!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||||
|
@ -109,317 +83,16 @@ app.post("/api/article/edit/:id", async (req, res) => {
|
||||||
const user = await authorize(token)
|
const user = await authorize(token)
|
||||||
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||||
|
|
||||||
const { title, content, image, sponsors } = req.body
|
const workers = await prisma.mitarbeiter.findMany({
|
||||||
if (!title || !content || !image || !req.params.id) return res.status(400).send(Errors.MISSING_ITEMS)
|
|
||||||
|
|
||||||
// sponsors is an array of sponsor IDs
|
|
||||||
|
|
||||||
const article = await prisma.articles.update({
|
|
||||||
where: {
|
|
||||||
ID: req.params.id
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
title,
|
|
||||||
sponsors: {
|
|
||||||
set: sponsors.map((sponsor: string) => ({
|
|
||||||
ID: sponsor
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
fs.writeFileSync(`/var/lib/rheinefuerrheine/artikel/${article.ID}.html`, content)
|
|
||||||
if (image !== "stale") 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: {
|
select: {
|
||||||
ID: true,
|
Vorname: true,
|
||||||
title: true,
|
Nachname: true,
|
||||||
views: true,
|
Geburtstag: true,
|
||||||
author: {
|
Anstelldatum: true,
|
||||||
select: {
|
|
||||||
ID: true,
|
|
||||||
username: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sponsors: true,
|
|
||||||
createdAt: true,
|
|
||||||
updatedAt: true,
|
|
||||||
}
|
|
||||||
}).catch(() => null)
|
|
||||||
|
|
||||||
if (!article) return res.status(404).send(Errors.NOT_FOUND)
|
|
||||||
|
|
||||||
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)
|
res.send(workers)
|
||||||
})
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
const content = fs.readFileSync(`/var/lib/rheinefuerrheine/artikel/${req.params.article}.banner`)
|
|
||||||
|
|
||||||
// its a base64 encoded image. The type is appended at the beginning of the string (e.g. data:image/png;base64,....) Extract the type and send it as content type header and the rest as the body
|
|
||||||
const type = content.toString().split(';')[0].split(':')[1]
|
|
||||||
res.setHeader('Content-Type', type)
|
|
||||||
res.send(Buffer.from(content.toString().split(';base64,')[1], 'base64'))
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
app.get('/api/sponsors/', async (req, res) => {
|
|
||||||
const sponsors = await prisma.sponsors.findMany({
|
|
||||||
orderBy: {
|
|
||||||
addedAt: 'desc'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
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) return res.status(400).send(Errors.MISSING_ITEMS)
|
|
||||||
|
|
||||||
const sponsor = await prisma.sponsors.update({
|
|
||||||
where: {
|
|
||||||
ID: req.params.id
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
url,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (logo) fs.writeFileSync(`/var/lib/rheinefuerrheine/sponsoren/${sponsor.ID}.logo`, logo)
|
|
||||||
if (banner) 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/sponsors/:id', async (req, res) => {
|
|
||||||
const sponsor = await prisma.sponsors.findUnique({
|
|
||||||
where: {
|
|
||||||
ID: req.params.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!sponsor) return res.status(404).send(Errors.NOT_FOUND)
|
|
||||||
|
|
||||||
res.send(sponsor)
|
|
||||||
})
|
|
||||||
|
|
||||||
app.get('/api/sponsors/logo/:id', async (req, res) => {
|
|
||||||
if (!fs.existsSync(`/var/lib/rheinefuerrheine/sponsoren/${req.params.id}.logo`)) return res.status(404).send(Errors.NOT_FOUND)
|
|
||||||
|
|
||||||
const content = fs.readFileSync(`/var/lib/rheinefuerrheine/sponsoren/${req.params.id}.logo`)
|
|
||||||
|
|
||||||
// its a base64 encoded image. The type is appended at the beginning of the string (e.g. data:image/png;base64,....) Extract the type and send it as content type header and the rest as the body
|
|
||||||
const type = content.toString().split(';')[0].split(':')[1]
|
|
||||||
res.setHeader('Content-Type', type)
|
|
||||||
res.send(Buffer.from(content.toString().split(';base64,')[1], 'base64'))
|
|
||||||
})
|
|
||||||
|
|
||||||
app.get('/api/sponsors/banner/:id', async (req, res) => {
|
|
||||||
if (!fs.existsSync(`/var/lib/rheinefuerrheine/sponsoren/${req.params.id}.banner`)) return res.status(404).send(Errors.NOT_FOUND)
|
|
||||||
|
|
||||||
const content = fs.readFileSync(`/var/lib/rheinefuerrheine/sponsoren/${req.params.id}.banner`)
|
|
||||||
|
|
||||||
// its a base64 encoded image. The type is appended at the beginning of the string (e.g. data:image/png;base64,....) Extract the type and send it as content type header and the rest as the body
|
|
||||||
const type = content.toString().split(';')[0].split(':')[1]
|
|
||||||
res.setHeader('Content-Type', type)
|
|
||||||
res.send(Buffer.from(content.toString().split(';base64,')[1], 'base64'))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get('/api/users', async (req, res) => {
|
app.get('/api/users', async (req, res) => {
|
||||||
|
@ -430,17 +103,12 @@ app.get('/api/users', async (req, res) => {
|
||||||
const user = await authorize(token)
|
const user = await authorize(token)
|
||||||
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
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({
|
const users = await prisma.users.findMany({
|
||||||
select: {
|
select: {
|
||||||
ID: true,
|
ID: true,
|
||||||
username: true,
|
username: true,
|
||||||
admin: true,
|
|
||||||
article_create: true,
|
|
||||||
article_manage: true,
|
|
||||||
sponsor_manage: true,
|
|
||||||
user_manage: true,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -455,7 +123,6 @@ app.get('/api/user/:id', async (req, res) => {
|
||||||
const user = await authorize(token)
|
const user = await authorize(token)
|
||||||
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
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 foundUser = await prisma.users.findUnique({
|
const foundUser = await prisma.users.findUnique({
|
||||||
where: {
|
where: {
|
||||||
|
@ -463,12 +130,7 @@ app.get('/api/user/:id', async (req, res) => {
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
ID: true,
|
ID: true,
|
||||||
username: true,
|
username: true
|
||||||
admin: true,
|
|
||||||
article_create: true,
|
|
||||||
article_manage: true,
|
|
||||||
sponsor_manage: true,
|
|
||||||
user_manage: true,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -485,21 +147,15 @@ app.post('/api/users/create', async (req, res) => {
|
||||||
const user = await authorize(token)
|
const user = await authorize(token)
|
||||||
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
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 { username, password, admin, article_create, article_manage, sponsor_manage, user_manage } = req.body
|
const { username, password } = req.body
|
||||||
if (!username || !password || admin === undefined || article_create === undefined || article_manage === undefined || sponsor_manage === undefined || user_manage === undefined) return res.status(400).send(Errors.MISSING_ITEMS)
|
if (!username || !password) return res.status(400).send(Errors.MISSING_ITEMS)
|
||||||
|
|
||||||
const newUser = await prisma.users.create({
|
const newUser = await prisma.users.create({
|
||||||
data: {
|
data: {
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
token: crypto.randomBytes(64).toString('hex'),
|
token: crypto.randomBytes(64).toString('hex'),
|
||||||
admin,
|
|
||||||
article_create,
|
|
||||||
article_manage,
|
|
||||||
sponsor_manage,
|
|
||||||
user_manage,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -514,7 +170,6 @@ app.patch('/api/users/edit/:id', async (req, res) => {
|
||||||
const user = await authorize(token)
|
const user = await authorize(token)
|
||||||
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
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 { username, password, admin, article_create, article_manage, sponsor_manage, user_manage } = req.body
|
const { username, password, admin, article_create, article_manage, sponsor_manage, user_manage } = req.body
|
||||||
if (!username || admin === undefined || article_create === undefined || article_manage === undefined || sponsor_manage === undefined || user_manage === undefined) return res.status(400).send(Errors.MISSING_ITEMS)
|
if (!username || admin === undefined || article_create === undefined || article_manage === undefined || sponsor_manage === undefined || user_manage === undefined) return res.status(400).send(Errors.MISSING_ITEMS)
|
||||||
|
@ -544,9 +199,6 @@ app.delete('/api/users/delete/:id', async (req, res) => {
|
||||||
|
|
||||||
const user = await authorize(token)
|
const user = await authorize(token)
|
||||||
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||||
|
|
||||||
if (!user.admin && !user.user_manage) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
|
||||||
|
|
||||||
await prisma.users.delete({
|
await prisma.users.delete({
|
||||||
where: {
|
where: {
|
||||||
ID: req.params.id
|
ID: req.params.id
|
||||||
|
@ -556,31 +208,8 @@ app.delete('/api/users/delete/:id', async (req, res) => {
|
||||||
res.send('OK')
|
res.send('OK')
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get('/api/stats', 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.count()
|
|
||||||
const users = await prisma.users.count()
|
|
||||||
const sponsors = await prisma.sponsors.count()
|
|
||||||
const views = await prisma.articles.aggregate({
|
|
||||||
_sum: {
|
|
||||||
views: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
res.send({
|
|
||||||
articles,
|
|
||||||
users,
|
|
||||||
sponsors,
|
|
||||||
views: views._sum.views
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
app.listen(process.env.PORT, () => {
|
app.listen(process.env.PORT, () => {
|
||||||
console.log(`Server is running on port ${process.env.PORT}`)
|
console.log(`Server is running on port ${process.env.PORT}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,30 @@
|
||||||
import { prisma } from '.'
|
import { prisma } from ".";
|
||||||
import crypto from 'crypto'
|
import crypto from "crypto";
|
||||||
import Logger from './logger';
|
import Logger from "./logger";
|
||||||
|
|
||||||
const logger = new Logger('Init');
|
const logger = new Logger("Init");
|
||||||
|
|
||||||
export default async function init() {
|
export default async function init() {
|
||||||
logger.info('Checking database...');
|
logger.info("Checking database...");
|
||||||
const users = await prisma.users.count();
|
const users = await prisma.users.count();
|
||||||
|
|
||||||
if (users === 0) {
|
if (users === 0) {
|
||||||
logger.warn('No users found, creating admin user...');
|
logger.warn("No users found, creating admin user...");
|
||||||
await prisma.$transaction([
|
await prisma.$transaction([
|
||||||
prisma.users.create({
|
prisma.users.create({
|
||||||
data: {
|
data: {
|
||||||
username: 'admin',
|
username: "admin",
|
||||||
password: crypto.createHash('sha256').update('rheine admin rheine').digest('hex'),
|
password: crypto
|
||||||
token: crypto.randomBytes(64).toString('hex'),
|
.createHash("sha256")
|
||||||
admin: true
|
.update("rheine admin rheine")
|
||||||
}
|
.digest("hex"),
|
||||||
})
|
token: crypto.randomBytes(64).toString("hex"),
|
||||||
]);
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
logger.info('Admin user created! Username: admin, Password: admin');
|
logger.info("Admin user created! Username: admin, Password: admin");
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Database is ready!');
|
logger.info("Database is ready!");
|
||||||
}
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
"@mui/icons-material": "^5.14.9",
|
"@mui/icons-material": "^5.14.9",
|
||||||
"@mui/lab": "^5.0.0-alpha.154",
|
"@mui/lab": "^5.0.0-alpha.154",
|
||||||
"@mui/material": "^5.14.10",
|
"@mui/material": "^5.14.10",
|
||||||
|
"@mui/x-data-grid": "^6.19.1",
|
||||||
"@types/node": "^16.18.52",
|
"@types/node": "^16.18.52",
|
||||||
"@types/react": "^18.2.22",
|
"@types/react": "^18.2.22",
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.2.7",
|
||||||
|
@ -22,6 +23,7 @@
|
||||||
"js-sha256": "^0.10.1",
|
"js-sha256": "^0.10.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-calendar": "^4.8.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-intersection-observer": "^9.5.2",
|
"react-intersection-observer": "^9.5.2",
|
||||||
"react-router-dom": "^6.16.0",
|
"react-router-dom": "^6.16.0",
|
||||||
|
@ -3645,6 +3647,31 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
||||||
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@mui/x-data-grid": {
|
||||||
|
"version": "6.19.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-6.19.1.tgz",
|
||||||
|
"integrity": "sha512-qtmt+XAOdxwb7p3vjk4HcfncAOe+0HzpcC6o6G+rzk9Hqq6MgG3yODrN9mRc/ONg0fD0eVlqDogXxNtJhcFmTQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.23.2",
|
||||||
|
"@mui/utils": "^5.14.16",
|
||||||
|
"clsx": "^2.0.0",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"reselect": "^4.1.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@mui/material": "^5.4.1",
|
||||||
|
"@mui/system": "^5.4.1",
|
||||||
|
"react": "^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
|
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
|
||||||
"version": "5.1.1-v1",
|
"version": "5.1.1-v1",
|
||||||
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
|
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
|
||||||
|
@ -4278,6 +4305,19 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
||||||
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
|
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/lodash": {
|
||||||
|
"version": "4.14.202",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz",
|
||||||
|
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/lodash.memoize": {
|
||||||
|
"version": "4.1.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash.memoize/-/lodash.memoize-4.1.9.tgz",
|
||||||
|
"integrity": "sha512-glY1nQuoqX4Ft8Uk+KfJudOD7DQbbEDF6k9XpGncaohW3RW4eSWBlx6AA0fZCrh40tZcQNH4jS/Oc59J6Eq+aw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/mime": {
|
"node_modules/@types/mime": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||||
|
@ -4782,6 +4822,14 @@
|
||||||
"@xtuc/long": "4.2.2"
|
"@xtuc/long": "4.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@wojtekmaj/date-utils": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-+i7+JmNiE/3c9FKxzWFi2IjRJ+KzZl1QPu6QNrsgaa2MuBgXvUy4gA1TVzf/JMdIIloB76xSKikTWuyYAIVLww==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/wojtekmaj/date-utils?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@xtuc/ieee754": {
|
"node_modules/@xtuc/ieee754": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||||
|
@ -8641,6 +8689,18 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/get-user-locale": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-VEvcsqKYx7zhZYC1CjecrDC5ziPSpl1gSm0qFFJhHSGDrSC+x4+p1KojWC/83QX//j476gFhkVXP/kNUc9q+bQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/lodash.memoize": "^4.1.7",
|
||||||
|
"lodash.memoize": "^4.1.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/wojtekmaj/get-user-locale?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "7.2.3",
|
"version": "7.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
@ -14491,6 +14551,31 @@
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-calendar": {
|
||||||
|
"version": "4.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-calendar/-/react-calendar-4.8.0.tgz",
|
||||||
|
"integrity": "sha512-qFgwo+p58sgv1QYMI1oGNaop90eJVKuHTZ3ZgBfrrpUb+9cAexxsKat0sAszgsizPMVo7vOXedV7Lqa0GQGMvA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@wojtekmaj/date-utils": "^1.1.3",
|
||||||
|
"clsx": "^2.0.0",
|
||||||
|
"get-user-locale": "^2.2.1",
|
||||||
|
"prop-types": "^15.6.0",
|
||||||
|
"warning": "^4.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/wojtekmaj/react-calendar?sponsor=1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-dev-utils": {
|
"node_modules/react-dev-utils": {
|
||||||
"version": "12.0.1",
|
"version": "12.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
|
||||||
|
@ -14951,6 +15036,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
|
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/reselect": {
|
||||||
|
"version": "4.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
|
||||||
|
"integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="
|
||||||
|
},
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.6",
|
"version": "1.22.6",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz",
|
||||||
|
@ -16894,6 +16984,14 @@
|
||||||
"makeerror": "1.0.12"
|
"makeerror": "1.0.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/warning": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
|
||||||
|
"dependencies": {
|
||||||
|
"loose-envify": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/watchpack": {
|
"node_modules/watchpack": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
"@mui/icons-material": "^5.14.9",
|
"@mui/icons-material": "^5.14.9",
|
||||||
"@mui/lab": "^5.0.0-alpha.154",
|
"@mui/lab": "^5.0.0-alpha.154",
|
||||||
"@mui/material": "^5.14.10",
|
"@mui/material": "^5.14.10",
|
||||||
|
"@mui/x-data-grid": "^6.19.1",
|
||||||
"@types/node": "^16.18.52",
|
"@types/node": "^16.18.52",
|
||||||
"@types/react": "^18.2.22",
|
"@types/react": "^18.2.22",
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.2.7",
|
||||||
|
@ -17,6 +18,7 @@
|
||||||
"js-sha256": "^0.10.1",
|
"js-sha256": "^0.10.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-calendar": "^4.8.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-intersection-observer": "^9.5.2",
|
"react-intersection-observer": "^9.5.2",
|
||||||
"react-router-dom": "^6.16.0",
|
"react-router-dom": "^6.16.0",
|
||||||
|
@ -28,7 +30,7 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build && cp -a build/* ../backend/public",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject"
|
||||||
},
|
},
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
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`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>Rheine Für Rheine</title>
|
<title>DWL Birthdays</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 149 KiB |
|
@ -3,22 +3,16 @@ import { Routes, Route } from "react-router-dom";
|
||||||
// @Pages
|
// @Pages
|
||||||
import LandingPage from "./pages/LandingPage";
|
import LandingPage from "./pages/LandingPage";
|
||||||
import LoginPage from "./pages/Login";
|
import LoginPage from "./pages/Login";
|
||||||
import Datenschutz from "./pages/Datenschutz";
|
|
||||||
import Impressum from "./pages/Impressum";
|
|
||||||
import NotFound from "./pages/404";
|
import NotFound from "./pages/404";
|
||||||
import AdminFrame from "./pages/AdminFrame";
|
import AdminFrame from "./pages/AdminFrame";
|
||||||
import Artikel from "./pages/Artikel";
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<LandingPage />} />
|
<Route path="/" element={<LandingPage />} />
|
||||||
<Route path="/login" element={<LoginPage />} />
|
<Route path="/login" element={<LoginPage />} />
|
||||||
<Route path="/datenschutz" element={<Datenschutz />} />
|
|
||||||
<Route path="/impressum" element={<Impressum />} />
|
|
||||||
<Route path="/admin/*" element={<AdminFrame />} />
|
<Route path="/admin/*" element={<AdminFrame />} />
|
||||||
<Route path="*" element={<NotFound />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
<Route path="/artikel/:id" element={<Artikel />} />
|
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,201 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,344 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
import { Avatar, Box, Button, Typography } from "@mui/material";
|
|
||||||
import { getBaseURL } from "../functions";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
|
|
||||||
function ArticleCard({ id, name }: { id: string; name: string }) {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "500px",
|
|
||||||
height: "300px",
|
|
||||||
|
|
||||||
backgroundColor: "#FFF",
|
|
||||||
|
|
||||||
borderRadius: "13px",
|
|
||||||
boxShadow: (theme) => `2px 5px 7px ${theme.palette.primary.light}`,
|
|
||||||
|
|
||||||
// clip the corners
|
|
||||||
overflow: "hidden",
|
|
||||||
cursor: "pointer",
|
|
||||||
transition: "all 0.2s ease-in-out",
|
|
||||||
|
|
||||||
"&:hover": {
|
|
||||||
boxShadow: (theme) => `2px 5px 7px ${theme.palette.primary.main}`,
|
|
||||||
transform: "scale(1.02)",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
navigate(`/artikel/${id}`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={`${getBaseURL()}/api/article/banner/${id}`}
|
|
||||||
alt=""
|
|
||||||
style={{
|
|
||||||
width: "500px",
|
|
||||||
height: "150px",
|
|
||||||
|
|
||||||
objectFit: "cover",
|
|
||||||
minWidth: "500px",
|
|
||||||
minHeight: "150px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
mt: "0px",
|
|
||||||
|
|
||||||
width: "100%",
|
|
||||||
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
margin="none"
|
|
||||||
sx={{
|
|
||||||
ml: "10px",
|
|
||||||
width: "100%",
|
|
||||||
|
|
||||||
fontFamily: "Lexend Variable",
|
|
||||||
fontSize: "38px",
|
|
||||||
fontWeight: 800,
|
|
||||||
fontStyle: "italic",
|
|
||||||
textAlign: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ArticleCard;
|
|
|
@ -1,209 +0,0 @@
|
||||||
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",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
style={{
|
|
||||||
color: "#fff",
|
|
||||||
fontWeight: "light",
|
|
||||||
fontSize: "1rem",
|
|
||||||
letterSpacing: "0.05rem",
|
|
||||||
lineHeight: "1.2",
|
|
||||||
textAlign: "center",
|
|
||||||
}}
|
|
||||||
href="https://ipmake.dev"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
© 2023 <br /> IPGSystems inc. All rights reserved
|
|
||||||
</a>
|
|
||||||
<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>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
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>;
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Box, Divider } from "@mui/material"
|
import { Box, Divider } from "@mui/material"
|
||||||
import SidebarElement from "./SidebarElement"
|
import SidebarElement from "./SidebarElement"
|
||||||
import { ArrowBack, Home, Logout, Newspaper, Person, Savings } from "@mui/icons-material"
|
import { ArrowBack, Home, Logout, Newspaper, Person, TableChartOutlined, Event } from "@mui/icons-material"
|
||||||
|
|
||||||
function Sidebar() {
|
function Sidebar() {
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ function Sidebar() {
|
||||||
top: "0",
|
top: "0",
|
||||||
left: "0",
|
left: "0",
|
||||||
|
|
||||||
backgroundColor: "#fff",
|
backgroundColor: "#121212",
|
||||||
boxShadow: "0 0 10px #00000055",
|
boxShadow: "0 0 10px #00000055",
|
||||||
|
|
||||||
zIndex: 1000,
|
zIndex: 1000,
|
||||||
|
@ -47,8 +47,8 @@ function Sidebar() {
|
||||||
gap: "2px",
|
gap: "2px",
|
||||||
}}>
|
}}>
|
||||||
<SidebarElement Title="Dashboard" Icon={Home} Path="/admin/dashboard" />
|
<SidebarElement Title="Dashboard" Icon={Home} Path="/admin/dashboard" />
|
||||||
<SidebarElement Title="Artikel" Icon={Newspaper} Path="/admin/artikel" />
|
<SidebarElement Title="Daten import" Icon={TableChartOutlined} Path="/admin/import" />
|
||||||
<SidebarElement Title="Sponsoren" Icon={Savings} Path="/admin/sponsoren" />
|
<SidebarElement Title="Kalender" Icon={Event} Path="/admin/Kalender" />
|
||||||
<SidebarElement Title="Benutzer" Icon={Person} Path="/admin/benutzer" />
|
<SidebarElement Title="Benutzer" Icon={Person} Path="/admin/benutzer" />
|
||||||
|
|
||||||
<SidebarElement Title="Zur Website" Icon={ArrowBack} Path="/" sx={{
|
<SidebarElement Title="Zur Website" Icon={ArrowBack} Path="/" sx={{
|
||||||
|
|
|
@ -47,7 +47,7 @@ function SidebarElement({
|
||||||
<Icon
|
<Icon
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "30px",
|
fontSize: "30px",
|
||||||
color: "#000000CC",
|
color: "#fff",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography
|
<Typography
|
||||||
|
@ -55,7 +55,7 @@ function SidebarElement({
|
||||||
fontFamily: "Lexend Variable",
|
fontFamily: "Lexend Variable",
|
||||||
fontSize: "20px",
|
fontSize: "20px",
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
color: "#000000CC",
|
color: "#fff",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Title}
|
{Title}
|
||||||
|
|
|
@ -1,153 +0,0 @@
|
||||||
import { Avatar, Box, Button, Typography } from "@mui/material";
|
|
||||||
import { getBaseURL } from "../functions";
|
|
||||||
|
|
||||||
function SponsorCard({
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
url,
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
url: string;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "500px",
|
|
||||||
height: "300px",
|
|
||||||
|
|
||||||
backgroundColor: "#FFF",
|
|
||||||
|
|
||||||
borderRadius: "13px",
|
|
||||||
boxShadow: theme => `2px 5px 7px ${theme.palette.primary.light}`,
|
|
||||||
|
|
||||||
// clip the corners
|
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={`${getBaseURL()}/api/sponsors/banner/${id}`}
|
|
||||||
alt=""
|
|
||||||
style={{
|
|
||||||
width: "500px",
|
|
||||||
height: "150px",
|
|
||||||
|
|
||||||
objectFit: "cover",
|
|
||||||
minWidth: "500px",
|
|
||||||
minHeight: "150px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
mt: "-75px",
|
|
||||||
|
|
||||||
width: "500px",
|
|
||||||
height: "225px",
|
|
||||||
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "flex-start",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "flex-start",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
}}>
|
|
||||||
<Avatar
|
|
||||||
variant="square"
|
|
||||||
src={`${getBaseURL()}/api/sponsors/logo/${id}`}
|
|
||||||
sx={{
|
|
||||||
width: "150px",
|
|
||||||
height: "150px",
|
|
||||||
|
|
||||||
borderRadius: "20px",
|
|
||||||
ml: "25px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
sx={{
|
|
||||||
width: "150px",
|
|
||||||
height: "50px",
|
|
||||||
|
|
||||||
mt: "10px",
|
|
||||||
ml: "25px",
|
|
||||||
|
|
||||||
color: "#fff",
|
|
||||||
|
|
||||||
borderRadius: "20px",
|
|
||||||
padding: "5px",
|
|
||||||
"& .MuiButton-label": {
|
|
||||||
p: "0px",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
window.open(url, "_blank");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Webseite
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<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
|
|
||||||
margin="none"
|
|
||||||
sx={{
|
|
||||||
ml: "10px",
|
|
||||||
width: "280px",
|
|
||||||
|
|
||||||
fontFamily: "Lexend Variable",
|
|
||||||
fontSize: "38px",
|
|
||||||
fontWeight: 800,
|
|
||||||
fontStyle: "italic",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</Typography>
|
|
||||||
<Typography
|
|
||||||
margin="none"
|
|
||||||
sx={{
|
|
||||||
height: "90px",
|
|
||||||
minHeight: "90px",
|
|
||||||
width: "280px",
|
|
||||||
|
|
||||||
fontFamily: "Lexend Variable",
|
|
||||||
fontSize: "16px",
|
|
||||||
fontWeight: 200,
|
|
||||||
fontStyle: "italic",
|
|
||||||
color: "#828282",
|
|
||||||
ml: "10px",
|
|
||||||
textAlign: "left",
|
|
||||||
verticalAlign: "top",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{description}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SponsorCard;
|
|
|
@ -1,270 +0,0 @@
|
||||||
import { Avatar, Box, Button, TextField } from "@mui/material";
|
|
||||||
import { useEffect, useRef, useState } from "react";
|
|
||||||
import { getBaseURL } from "../functions";
|
|
||||||
|
|
||||||
function SponsorCardEditable({
|
|
||||||
onSave,
|
|
||||||
edit,
|
|
||||||
}: {
|
|
||||||
onSave: (result: {
|
|
||||||
name: string,
|
|
||||||
description: string,
|
|
||||||
logo: string | null,
|
|
||||||
banner: string | null,
|
|
||||||
url: string,
|
|
||||||
}) => void;
|
|
||||||
edit?: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
description: string,
|
|
||||||
url: string,
|
|
||||||
};
|
|
||||||
}) {
|
|
||||||
const logoRef = useRef<HTMLInputElement>(null);
|
|
||||||
const bannerRef = useRef<HTMLInputElement>(null);
|
|
||||||
|
|
||||||
const [logo, setLogo] = useState<string>("https://placehold.co/150x150");
|
|
||||||
const [banner, setBanner] = useState<string>("https://placehold.co/500x150");
|
|
||||||
|
|
||||||
const [name, setName] = useState<string>("");
|
|
||||||
const [description, setDescription] = useState<string>("");
|
|
||||||
|
|
||||||
const [url, setUrl] = useState<string>("");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!edit) return;
|
|
||||||
|
|
||||||
setName(edit.name);
|
|
||||||
setDescription(edit.description);
|
|
||||||
setUrl(edit.url);
|
|
||||||
setLogo(`${getBaseURL()}/api/sponsors/logo/${edit.id}`);
|
|
||||||
setBanner(`${getBaseURL()}/api/sponsors/banner/${edit.id}`);
|
|
||||||
}, [edit]);
|
|
||||||
|
|
||||||
const valid = () => {
|
|
||||||
if (!name) return false;
|
|
||||||
if (!description) return false;
|
|
||||||
if (!logo) return false;
|
|
||||||
if (!banner) return false;
|
|
||||||
if (!url) return false;
|
|
||||||
|
|
||||||
if (logo.startsWith("https://placehold.co/")) return false;
|
|
||||||
if (banner.startsWith("https://placehold.co/")) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
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",
|
|
||||||
|
|
||||||
borderRadius: "13px",
|
|
||||||
border: "1px solid #00000033",
|
|
||||||
|
|
||||||
// clip the corners
|
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<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",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextField
|
|
||||||
multiline
|
|
||||||
margin="none"
|
|
||||||
variant="standard"
|
|
||||||
fullWidth
|
|
||||||
value={name}
|
|
||||||
placeholder="Name"
|
|
||||||
sx={{
|
|
||||||
ml: "10px",
|
|
||||||
width: "280px",
|
|
||||||
|
|
||||||
"& .MuiInputBase-root": {
|
|
||||||
fontFamily: "Lexend Variable",
|
|
||||||
fontSize: "38px",
|
|
||||||
fontWeight: 800,
|
|
||||||
fontStyle: "italic",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (e.target.value.length >= 12) return;
|
|
||||||
if (e.target.value.includes("\n")) return;
|
|
||||||
setName(e.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextField
|
|
||||||
multiline
|
|
||||||
margin="none"
|
|
||||||
variant="standard"
|
|
||||||
fullWidth
|
|
||||||
value={description}
|
|
||||||
placeholder="Beschreibung"
|
|
||||||
sx={{
|
|
||||||
height: "90px",
|
|
||||||
minHeight: "90px",
|
|
||||||
width: "280px",
|
|
||||||
|
|
||||||
"& .MuiInputBase-root": {
|
|
||||||
display: "inline",
|
|
||||||
fontFamily: "Lexend Variable",
|
|
||||||
fontSize: "16px",
|
|
||||||
fontWeight: 200,
|
|
||||||
fontStyle: "italic",
|
|
||||||
color: "#828282",
|
|
||||||
ml: "10px",
|
|
||||||
height: "90px",
|
|
||||||
minHeight: "90px",
|
|
||||||
textAlign: "left",
|
|
||||||
verticalAlign: "top",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (e.target.value.length >= 100) return;
|
|
||||||
if (e.target.value.split("\n").length > 3) return;
|
|
||||||
setDescription(e.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<TextField
|
|
||||||
margin="none"
|
|
||||||
variant="standard"
|
|
||||||
fullWidth
|
|
||||||
value={url}
|
|
||||||
placeholder="URL"
|
|
||||||
sx={{
|
|
||||||
mt: "20px",
|
|
||||||
width: "80%",
|
|
||||||
}}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (e.target.value.length >= 128) return;
|
|
||||||
if (e.target.value.includes("\n")) return;
|
|
||||||
setUrl(e.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
sx={{ width: "80%" }}
|
|
||||||
disabled={!valid()}
|
|
||||||
onClick={() => {
|
|
||||||
onSave({
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
logo,
|
|
||||||
banner,
|
|
||||||
url,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Speichern
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SponsorCardEditable;
|
|
|
@ -1,53 +0,0 @@
|
||||||
import { Box, Grid, Avatar } from "@mui/material";
|
|
||||||
|
|
||||||
export function SponsorImageSmall({
|
|
||||||
name,
|
|
||||||
image,
|
|
||||||
description,
|
|
||||||
link,
|
|
||||||
onRemove,
|
|
||||||
}: {
|
|
||||||
name: string;
|
|
||||||
image: string;
|
|
||||||
description: string;
|
|
||||||
link: string;
|
|
||||||
onRemove?: () => void;
|
|
||||||
}): JSX.Element {
|
|
||||||
return (
|
|
||||||
<Grid item>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Avatar
|
|
||||||
src={image}
|
|
||||||
alt=""
|
|
||||||
sx={{
|
|
||||||
height: "75px",
|
|
||||||
width: "75px",
|
|
||||||
borderRadius: "20px",
|
|
||||||
|
|
||||||
...(onRemove && {
|
|
||||||
cursor: "crosshair",
|
|
||||||
|
|
||||||
"&:hover": {
|
|
||||||
filter: "brightness(0.5)",
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
...(!onRemove && {
|
|
||||||
cursor: "pointer",
|
|
||||||
}),
|
|
||||||
}}
|
|
||||||
onClick={
|
|
||||||
onRemove ||
|
|
||||||
(() => {
|
|
||||||
window.open(link, "_blank");
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,210 +0,0 @@
|
||||||
import { Close } from "@mui/icons-material";
|
|
||||||
import {
|
|
||||||
Backdrop,
|
|
||||||
Box,
|
|
||||||
CircularProgress,
|
|
||||||
IconButton,
|
|
||||||
Typography,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import SponsorCardEditable from "./SponsorCardEditable";
|
|
||||||
import axios from "axios";
|
|
||||||
import { getBaseURL } from "../functions";
|
|
||||||
|
|
||||||
function SponsorModal({
|
|
||||||
id,
|
|
||||||
onClose,
|
|
||||||
}: {
|
|
||||||
id: string | null;
|
|
||||||
onClose?: () => void;
|
|
||||||
}) {
|
|
||||||
const [saving, setSaving] = useState<boolean>(false);
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
const [data, setData] = useState<Types.Sponsor | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setData(null);
|
|
||||||
if(!id) return;
|
|
||||||
if (id === "create") return;
|
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
axios
|
|
||||||
.get(`${getBaseURL()}/api/sponsors/${id}`)
|
|
||||||
.then((res) => {
|
|
||||||
setData(res.data);
|
|
||||||
setLoading(false);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
onClose?.();
|
|
||||||
});
|
|
||||||
}, [id]);
|
|
||||||
|
|
||||||
if (!id) return <></>;
|
|
||||||
if (loading)
|
|
||||||
return (
|
|
||||||
<Backdrop
|
|
||||||
open={true}
|
|
||||||
sx={{
|
|
||||||
zIndex: 2000,
|
|
||||||
flexDirection: "column",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CircularProgress />
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontFamily: "Lexend Variable",
|
|
||||||
fontSize: "20px",
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: "#fff",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Lade...
|
|
||||||
</Typography>
|
|
||||||
</Backdrop>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{saving && (
|
|
||||||
<Backdrop
|
|
||||||
open={true}
|
|
||||||
sx={{
|
|
||||||
zIndex: 2000,
|
|
||||||
flexDirection: "column",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CircularProgress />
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontFamily: "Lexend Variable",
|
|
||||||
fontSize: "20px",
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: "#fff",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Speichere...
|
|
||||||
</Typography>
|
|
||||||
</Backdrop>
|
|
||||||
)}
|
|
||||||
<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 { id === "create" ? "Erstellen" : "Bearbeiten" }
|
|
||||||
</Typography>
|
|
||||||
<IconButton onClick={onClose}>
|
|
||||||
<Close />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<SponsorCardEditable
|
|
||||||
edit={(id && id !== "create" && {
|
|
||||||
id: id,
|
|
||||||
name: data?.name as string,
|
|
||||||
description: data?.description as string,
|
|
||||||
url: data?.url as string,
|
|
||||||
}) || undefined}
|
|
||||||
onSave={async (result) => {
|
|
||||||
setSaving(true);
|
|
||||||
if (id === "create") {
|
|
||||||
axios
|
|
||||||
.post(
|
|
||||||
`${getBaseURL()}/api/sponsors/create`,
|
|
||||||
{
|
|
||||||
name: result.name,
|
|
||||||
description: result.description,
|
|
||||||
url: result.url,
|
|
||||||
logo: result.logo,
|
|
||||||
banner: result.banner,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${localStorage.getItem(
|
|
||||||
"token"
|
|
||||||
)}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
window.location.reload();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
result.logo = result.logo?.includes("http")
|
|
||||||
? null
|
|
||||||
: result.logo;
|
|
||||||
result.banner = result.banner?.includes("http")
|
|
||||||
? null
|
|
||||||
: result.banner;
|
|
||||||
|
|
||||||
axios
|
|
||||||
.patch(
|
|
||||||
`${getBaseURL()}/api/sponsors/edit/${id}`,
|
|
||||||
{
|
|
||||||
name: result.name,
|
|
||||||
description: result.description,
|
|
||||||
url: result.url,
|
|
||||||
logo: result.logo,
|
|
||||||
banner: result.banner,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${localStorage.getItem(
|
|
||||||
"token"
|
|
||||||
)}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
window.location.reload();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Backdrop>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SponsorModal;
|
|
|
@ -1,170 +0,0 @@
|
||||||
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="/#sponsors">Sponsoren</NavLink>
|
|
||||||
<NavLink to="/#about">Über uns</NavLink>
|
|
||||||
<NavLink to="/#info">Für Sie</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",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
href="https://www.rheine-tourismus.de/media/www.rheine-tourismus.de/org/med_1760/10298_strassenparty-flyer.n.pdf"
|
|
||||||
>
|
|
||||||
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;
|
|
|
@ -1,5 +1,5 @@
|
||||||
export function getBaseURL(): string {
|
export function getBaseURL(): string {
|
||||||
if(process.env.NODE_ENV === "development") return "http://localhost:3001"
|
if(process.env.NODE_ENV === "development") return "http://100.108.94.138:3001"
|
||||||
else return ""
|
else return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,11 +10,12 @@ import '@fontsource-variable/lexend';
|
||||||
|
|
||||||
const theme = createTheme({
|
const theme = createTheme({
|
||||||
palette: {
|
palette: {
|
||||||
|
mode: "dark",
|
||||||
primary: {
|
primary: {
|
||||||
main: "#6f9c01",
|
main: "#003263",
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
main: "#00ACD3",
|
main: "#9bc9e5",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// make the buttons not rounded
|
// make the buttons not rounded
|
||||||
|
|
|
@ -2,11 +2,10 @@ import { Box } from "@mui/material";
|
||||||
import { Route, Routes } from "react-router-dom";
|
import { Route, Routes } from "react-router-dom";
|
||||||
import Sidebar from "../components/Sidebar";
|
import Sidebar from "../components/Sidebar";
|
||||||
import Dashboard from "./admin/Dashboard";
|
import Dashboard from "./admin/Dashboard";
|
||||||
import Artikel from "./admin/Artikel";
|
|
||||||
import Sponsoren from "./admin/Sponsoren";
|
|
||||||
import Benutzer from "./admin/Benutzer";
|
import Benutzer from "./admin/Benutzer";
|
||||||
import ArticleEditor from "./admin/ArticleEditor";
|
|
||||||
import Logout from "./admin/Logout";
|
import Logout from "./admin/Logout";
|
||||||
|
import Import from "./admin/Import";
|
||||||
|
import Kalender from "./admin/Kalender";
|
||||||
|
|
||||||
function AdminFrame() {
|
function AdminFrame() {
|
||||||
return (
|
return (
|
||||||
|
@ -30,12 +29,10 @@ function AdminFrame() {
|
||||||
}}>
|
}}>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/dashboard" element={<Dashboard />} />
|
<Route path="/dashboard" element={<Dashboard />} />
|
||||||
<Route path="/artikel" element={<Artikel />} />
|
|
||||||
<Route path="/sponsoren" element={<Sponsoren />} />
|
|
||||||
<Route path="/benutzer" element={<Benutzer />} />
|
<Route path="/benutzer" element={<Benutzer />} />
|
||||||
<Route path="/editor" element={<ArticleEditor />} />
|
|
||||||
<Route path="/editor/:id" element={<ArticleEditor />} />
|
|
||||||
<Route path="/logout" element={<Logout />} />
|
<Route path="/logout" element={<Logout />} />
|
||||||
|
<Route path="/import" element={<Import />} />
|
||||||
|
<Route path="/kalender" element={<Kalender />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,180 +0,0 @@
|
||||||
/* eslint-disable jsx-a11y/alt-text */
|
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import TopBar from "../components/TopBar";
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
CircularProgress,
|
|
||||||
Grid,
|
|
||||||
Tooltip,
|
|
||||||
Typography,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { getBaseURL } from "../functions";
|
|
||||||
import { useEffect, useRef, useState } from "react";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
import "./Artikel.css";
|
|
||||||
import "suneditor/dist/css/suneditor.min.css";
|
|
||||||
import { SponsorImageSmall } from "../components/SponsorImageSmall";
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!id) return;
|
|
||||||
|
|
||||||
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={`${getBaseURL()}/api/article/banner/${id}`}
|
|
||||||
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
|
|
||||||
spacing={2}
|
|
||||||
sx={{
|
|
||||||
pb: "20px",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "flex-start",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{article?.sponsors.map((sponsor) => (
|
|
||||||
<Tooltip title={sponsor.name}>
|
|
||||||
<SponsorImageSmall
|
|
||||||
name={sponsor.name}
|
|
||||||
image={`${getBaseURL()}/api/sponsors/logo/${sponsor.ID}`}
|
|
||||||
description={sponsor.description}
|
|
||||||
link={sponsor.url}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
))}
|
|
||||||
</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;
|
|
|
@ -1,317 +0,0 @@
|
||||||
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;
|
|
|
@ -1,142 +0,0 @@
|
||||||
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;
|
|
|
@ -1,16 +1,10 @@
|
||||||
import { Typography, Button, Box, Grid, CircularProgress } from "@mui/material";
|
import { Typography, Button, Box, Grid, CircularProgress } from "@mui/material";
|
||||||
import { DescriptionOutlined, BadgeOutlined } from "@mui/icons-material";
|
import { 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 { useTheme } from "@mui/material";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import SponsorCard from "../components/SponsorCard";
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { getBaseURL } from "../functions";
|
import { getBaseURL } from "../functions";
|
||||||
import ArticleCard from "../components/ArticleCard";
|
|
||||||
|
|
||||||
function LandingPage() {
|
function LandingPage() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
@ -24,7 +18,6 @@ function LandingPage() {
|
||||||
}, [location]);
|
}, [location]);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBar />
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
@ -133,155 +126,8 @@ function LandingPage() {
|
||||||
Sponsor Werden!
|
Sponsor Werden!
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Sponsors />
|
|
||||||
<AboutSection />
|
|
||||||
<AboutSection2 />
|
|
||||||
<Articles />
|
|
||||||
<Footer />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LandingPage;
|
export default LandingPage;
|
||||||
|
|
||||||
function Sponsors(): JSX.Element {
|
|
||||||
const [data, setData] = useState<Types.Sponsor[] | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
axios.get(`${getBaseURL()}/api/sponsors/`).then((res) => {
|
|
||||||
setData(res.data);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (data === null)
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
px: "10px",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CircularProgress />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Grid
|
|
||||||
id="sponsors"
|
|
||||||
container
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
py: "10vh",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontWeight: "bolder",
|
|
||||||
fontFamily: "Lexend Variable",
|
|
||||||
fontSize: "4rem",
|
|
||||||
letterSpacing: "0.05rem",
|
|
||||||
lineHeight: "1.2",
|
|
||||||
textAlign: "center",
|
|
||||||
color: "primary.main",
|
|
||||||
mb: "5vh",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Unsere Sponsoren
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
{data.map((sponsor) => (
|
|
||||||
<Grid
|
|
||||||
item
|
|
||||||
xs
|
|
||||||
key={sponsor.ID}
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SponsorCard
|
|
||||||
id={sponsor.ID}
|
|
||||||
name={sponsor.name}
|
|
||||||
description={sponsor.description}
|
|
||||||
url={sponsor.url}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Articles(): JSX.Element {
|
|
||||||
const [data, setData] = useState<Types.Article[] | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
axios.get(`${getBaseURL()}/api/articles/public`).then((res) => {
|
|
||||||
setData(res.data);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (data === null)
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
px: "10px",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CircularProgress />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Grid
|
|
||||||
id="sponsors"
|
|
||||||
container
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
py: "10vh",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontWeight: "bolder",
|
|
||||||
fontFamily: "Lexend Variable",
|
|
||||||
fontSize: "4rem",
|
|
||||||
letterSpacing: "0.05rem",
|
|
||||||
lineHeight: "1.2",
|
|
||||||
textAlign: "center",
|
|
||||||
color: "primary.main",
|
|
||||||
mb: "5vh",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Aktuelles
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
{data.map((sponsor) => (
|
|
||||||
<Grid
|
|
||||||
item
|
|
||||||
xs
|
|
||||||
key={sponsor.ID}
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ArticleCard
|
|
||||||
id={sponsor.ID}
|
|
||||||
name={sponsor.title}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,431 +0,0 @@
|
||||||
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,
|
|
||||||
Menu,
|
|
||||||
Autocomplete,
|
|
||||||
TextField,
|
|
||||||
Avatar,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
|
||||||
import { AddBox } from "@mui/icons-material";
|
|
||||||
import { getBaseURL } from "../../functions";
|
|
||||||
import axios from "axios";
|
|
||||||
import { SponsorImageSmall } from "../../components/SponsorImageSmall";
|
|
||||||
|
|
||||||
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 [sponsorsAvail, setSponsorsAvail] = useState<Types.Sponsor[]>([]);
|
|
||||||
const [sponsorsSelected, setSponsorsSelected] = useState<Types.Sponsor[]>([]);
|
|
||||||
|
|
||||||
const sponsorsSelectedRef = useRef<Types.Sponsor[]>([]);
|
|
||||||
|
|
||||||
sponsorsSelectedRef.current = sponsorsSelected;
|
|
||||||
|
|
||||||
const [sponsorMenuEl, setSponsorMenuEl] = useState<null | HTMLElement>(null);
|
|
||||||
|
|
||||||
const loadbanner = () => {
|
|
||||||
if (!id) setBanner("https://placehold.co/1920x1080");
|
|
||||||
else setBanner(`${getBaseURL()}/api/article/banner/${id}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const save = async (content: string) => {
|
|
||||||
const title = document.getElementById("article-title")?.innerText;
|
|
||||||
let banner = document
|
|
||||||
.getElementById("articleBanner-content")
|
|
||||||
?.getAttribute("src");
|
|
||||||
if (!title) return false;
|
|
||||||
if (!banner) return false;
|
|
||||||
|
|
||||||
if (banner.startsWith("http")) banner = "stale";
|
|
||||||
|
|
||||||
setSaving(true);
|
|
||||||
const res = await axios
|
|
||||||
.post(
|
|
||||||
!id
|
|
||||||
? `${getBaseURL()}/api/article/create`
|
|
||||||
: `${getBaseURL()}/api/article/edit/${id}`,
|
|
||||||
{
|
|
||||||
title: title,
|
|
||||||
sponsors: sponsorsSelectedRef.current.map((e) => e.ID),
|
|
||||||
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(() => {
|
|
||||||
axios.get(`${getBaseURL()}/api/sponsors`).then((res) => {
|
|
||||||
if (!res.data) return;
|
|
||||||
|
|
||||||
setSponsorsAvail(res.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
setSponsorsSelected(res.data.sponsors);
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<>
|
|
||||||
<Menu
|
|
||||||
anchorEl={sponsorMenuEl}
|
|
||||||
open={Boolean(sponsorMenuEl)}
|
|
||||||
onClose={() => {
|
|
||||||
setSponsorMenuEl(null);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Autocomplete
|
|
||||||
options={sponsorsAvail.filter(e => !sponsorsSelected.map(e => e.ID).includes(e.ID))}
|
|
||||||
getOptionLabel={(option) => option.name}
|
|
||||||
renderOption={(props, option) => (
|
|
||||||
<Box
|
|
||||||
component="li"
|
|
||||||
sx={{ "& > img": { mr: 2, flexShrink: 0 } }}
|
|
||||||
{...props}
|
|
||||||
onClick={() => {
|
|
||||||
sponsorsSelected.push(option as Types.Sponsor);
|
|
||||||
setSponsorsSelected(sponsorsSelected);
|
|
||||||
setSponsorMenuEl(null);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Avatar
|
|
||||||
src={`${getBaseURL()}/api/sponsors/logo/${option.ID}`}
|
|
||||||
sx={{
|
|
||||||
width: "50px",
|
|
||||||
height: "50px",
|
|
||||||
borderRadius: "20px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{option.name}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
renderInput={(params) => (
|
|
||||||
<TextField
|
|
||||||
{...params}
|
|
||||||
sx={{
|
|
||||||
width: "300px",
|
|
||||||
}}
|
|
||||||
fullWidth
|
|
||||||
label="Sponsoren"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Menu>
|
|
||||||
{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",
|
|
||||||
width: "100vw",
|
|
||||||
position: "absolute",
|
|
||||||
top: "0px",
|
|
||||||
zIndex: -1,
|
|
||||||
objectFit: "cover",
|
|
||||||
minWidth: "100vw",
|
|
||||||
minHeight: "100vh",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</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",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Titel
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Grid
|
|
||||||
container
|
|
||||||
spacing={2}
|
|
||||||
sx={{
|
|
||||||
pb: "20px",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "flex-start",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{sponsorsSelected.map((sponsor) => (
|
|
||||||
<SponsorImageSmall
|
|
||||||
name={sponsor.name}
|
|
||||||
image={`${getBaseURL()}/api/sponsors/logo/${sponsor.ID}`}
|
|
||||||
description={sponsor.description}
|
|
||||||
link={sponsor.url}
|
|
||||||
onRemove={() => {
|
|
||||||
setSponsorsSelected(
|
|
||||||
sponsorsSelected.filter((e) => e.ID !== sponsor.ID)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<Grid item>
|
|
||||||
<AddBox
|
|
||||||
fontSize="large"
|
|
||||||
sx={{
|
|
||||||
cursor: "pointer",
|
|
||||||
|
|
||||||
transition: "all 0.2s ease-in-out",
|
|
||||||
"&:hover": {
|
|
||||||
color: "#000000AA",
|
|
||||||
transform: "scale(1.1)",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onClick={(e) => {
|
|
||||||
setSponsorMenuEl(e.currentTarget as any);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</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;
|
|
||||||
|
|
||||||
|
|
|
@ -1,245 +0,0 @@
|
||||||
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;
|
|
|
@ -4,31 +4,64 @@ import { OverridableComponent } from "@mui/material/OverridableComponent";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { getBaseURL } from "../../functions";
|
import { getBaseURL } from "../../functions";
|
||||||
|
import { DataGrid, GridColDef, GridValueGetterParams } from "@mui/x-data-grid";
|
||||||
|
import moment from "moment";
|
||||||
|
|
||||||
|
const columns: GridColDef[] = [
|
||||||
|
{
|
||||||
|
field: "Vorname",
|
||||||
|
headerName: "Vorname",
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "Nachname",
|
||||||
|
headerName: "Nachname",
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "Anstelldatum",
|
||||||
|
headerName: "Anstelldatum",
|
||||||
|
type: "date",
|
||||||
|
width: 160,
|
||||||
|
valueFormatter: params => moment(params.value).format("DD.MM.YYYY")
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "Geburtstag",
|
||||||
|
headerName: "Geburtstag",
|
||||||
|
type: "date",
|
||||||
|
width: 160,
|
||||||
|
valueFormatter: params => moment(params.value).format("DD.MM.YYYY")
|
||||||
|
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
function Dashboard() {
|
function Dashboard() {
|
||||||
const [stats, setStats] = useState<{
|
const [workers, setWorkers] = useState<Types.Worker[]>([]);
|
||||||
users: number;
|
|
||||||
articles: number;
|
|
||||||
sponsors: number;
|
|
||||||
views: number;
|
|
||||||
}>({
|
|
||||||
users: 0,
|
|
||||||
articles: 0,
|
|
||||||
sponsors: 0,
|
|
||||||
views: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
axios.get(`${getBaseURL()}/api/stats`, {
|
axios
|
||||||
headers: {
|
.get(`${getBaseURL()}/api/workers`, {
|
||||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
headers: {
|
||||||
},
|
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||||
}).then((response) => {
|
},
|
||||||
console.log(response.data);
|
})
|
||||||
setStats(response.data);
|
.then((res) => {
|
||||||
});
|
setWorkers(res.data);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const rows = workers.map((worker, index) => ({
|
||||||
|
id: index,
|
||||||
|
Vorname: worker.Vorname,
|
||||||
|
Nachname: worker.Nachname,
|
||||||
|
Geburtstag: worker.Geburtstag,
|
||||||
|
Anstelldatum: worker.Anstelldatum,
|
||||||
|
}))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -44,100 +77,39 @@ function Dashboard() {
|
||||||
px: "30px",
|
px: "30px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Grid container spacing={1}>
|
<Typography variant="h4" sx={{ fontWeight: "bold" }}>
|
||||||
<StatDisplay Title="Benutzer" Icon={Person} Value={stats.users.toFixed(0)} />
|
Nächste Geburtstage
|
||||||
<StatDisplay Title="Artikel" Icon={Newspaper} Value={stats.articles.toFixed(0)} />
|
</Typography>
|
||||||
<StatDisplay Title="Sponsoren" Icon={Savings} Value={stats.sponsors.toFixed(0)} />
|
|
||||||
<StatDisplay Title="Views" Icon={Visibility} Value={stats.views.toFixed(0)} />
|
<DataGrid
|
||||||
</Grid>
|
rows={rows}
|
||||||
|
columns={columns}
|
||||||
|
sx={{
|
||||||
|
fontSize: "1rem",
|
||||||
|
marginTop: "1rem",
|
||||||
|
borderRadius: "20px",
|
||||||
|
width: "100%",
|
||||||
|
height: "auto",
|
||||||
|
}}
|
||||||
|
initialState={{
|
||||||
|
sorting: {
|
||||||
|
sortModel: [
|
||||||
|
{
|
||||||
|
field: 'commodity',
|
||||||
|
sort: 'asc',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
paginationModel: {
|
||||||
|
pageSize: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
checkboxSelection
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Dashboard;
|
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,107 @@
|
||||||
|
import React, { useState, ChangeEvent, FormEvent } from "react";
|
||||||
|
import { Box, Button, Typography } from "@mui/material";
|
||||||
|
import { DataGrid, GridColDef } from "@mui/x-data-grid";
|
||||||
|
import moment from "moment";
|
||||||
|
|
||||||
|
function Import() {
|
||||||
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
const [csvData, setCsvData] = useState<
|
||||||
|
Array<{ [key: string]: string } | null>
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const fileReader = new FileReader();
|
||||||
|
|
||||||
|
const handleOnChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (e.target.files) {
|
||||||
|
setFile(e.target.files[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const csvFileToArray = (string: string) => {
|
||||||
|
const csvRows = string.split("\n");
|
||||||
|
const csvHeader = csvRows[0].split(";");
|
||||||
|
const newArray = csvRows
|
||||||
|
.slice(1)
|
||||||
|
.map((row) => {
|
||||||
|
const values = row.split(";");
|
||||||
|
if (values.length === csvHeader.length) {
|
||||||
|
const obj: { [key: string]: string } = {};
|
||||||
|
csvHeader.forEach((header, index) => {
|
||||||
|
obj[header] = values[index] ? values[index].replace(/"/g, "") : "";
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
setCsvData(newArray);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnSubmit = (e: FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
fileReader.onload = function (event) {
|
||||||
|
if (event.target?.result) {
|
||||||
|
const text = event.target.result.toString();
|
||||||
|
csvFileToArray(text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fileReader.readAsText(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ textAlign: "center" }}>
|
||||||
|
<Typography variant="h1">CSV IMPORT</Typography>
|
||||||
|
<form>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="csvFileInput"
|
||||||
|
accept=".csv"
|
||||||
|
onChange={handleOnChange}
|
||||||
|
/>
|
||||||
|
<Button onClick={(e) => handleOnSubmit(e)}>IMPORT CSV</Button>
|
||||||
|
</form>
|
||||||
|
<br />
|
||||||
|
{csvData.length > 0 ? (
|
||||||
|
<div>
|
||||||
|
<Typography variant="h4">Imported Data</Typography>
|
||||||
|
<DataGrid
|
||||||
|
rows={csvData.map((row, index) => ({ id: index, ...row }))}
|
||||||
|
columns={
|
||||||
|
csvData.length > 0
|
||||||
|
? Object.keys(csvData[0] || {}).map(
|
||||||
|
(header) =>
|
||||||
|
({
|
||||||
|
field: header,
|
||||||
|
headerName: header.replace(/"/g, ""),
|
||||||
|
width: 150,
|
||||||
|
valueFormatter(params) {
|
||||||
|
if (params.value) {
|
||||||
|
if (moment(params.value, "DD/MM/YYYY").isValid()) {
|
||||||
|
return moment(params.value).format(
|
||||||
|
"DD/MM/YYYY"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params.value;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
} as GridColDef)
|
||||||
|
)
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p>No data to display</p>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Import;
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function Kalender() {
|
||||||
|
return (
|
||||||
|
<div className="container">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default Kalender;
|
|
@ -1,232 +0,0 @@
|
||||||
import {
|
|
||||||
ManageAccounts,
|
|
||||||
DomainDisabledRounded,
|
|
||||||
DomainAddRounded,
|
|
||||||
} from "@mui/icons-material";
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Divider,
|
|
||||||
IconButton,
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
Typography,
|
|
||||||
Tooltip,
|
|
||||||
Avatar,
|
|
||||||
Backdrop,
|
|
||||||
CircularProgress,
|
|
||||||
} from "@mui/material";
|
|
||||||
import SponsorModal from "../../components/SponsorModal";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import axios from "axios";
|
|
||||||
import { getBaseURL } from "../../functions";
|
|
||||||
import moment from "moment";
|
|
||||||
|
|
||||||
function Sponsoren() {
|
|
||||||
const [modalID, setModelID] = useState<string | null>(null);
|
|
||||||
|
|
||||||
const [sponsors, setSponsors] = useState<Types.Sponsor[]>([]);
|
|
||||||
const [loaded, setLoaded] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setLoaded(false);
|
|
||||||
axios.get(`${getBaseURL()}/api/sponsors/`).then((res) => {
|
|
||||||
setSponsors(res.data);
|
|
||||||
setLoaded(true);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Backdrop
|
|
||||||
open={!loaded}
|
|
||||||
sx={{
|
|
||||||
zIndex: 2000,
|
|
||||||
color: "#fff",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CircularProgress
|
|
||||||
sx={{
|
|
||||||
color: "white",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Backdrop>
|
|
||||||
<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="50px">
|
|
||||||
{" "}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align="left" width="100%">
|
|
||||||
Name
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align="right">Datum</TableCell>
|
|
||||||
<TableCell align="right">Optionen</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
|
||||||
{sponsors.map((sponsor) => (
|
|
||||||
<TableRow key={sponsor.ID}>
|
|
||||||
<TableCell align="left" width="50px" padding="none">
|
|
||||||
<Avatar
|
|
||||||
src={`${getBaseURL()}/api/sponsors/logo/${sponsor.ID}`}
|
|
||||||
alt="logo"
|
|
||||||
style={{
|
|
||||||
width: "50px",
|
|
||||||
height: "50px",
|
|
||||||
borderRadius: "20px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align="left">{sponsor.name}</TableCell>
|
|
||||||
<TableCell align="right" width="200px">
|
|
||||||
{moment(sponsor.addedAt).locale("de").format("ll")}
|
|
||||||
</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",
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setModelID(sponsor.ID);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ManageAccounts />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip title="Sponsor löschen">
|
|
||||||
<IconButton
|
|
||||||
sx={{
|
|
||||||
width: "30px",
|
|
||||||
height: "30px",
|
|
||||||
|
|
||||||
padding: "5px",
|
|
||||||
backgroundColor: "#fff",
|
|
||||||
borderRadius: "5px",
|
|
||||||
"&:hover": {
|
|
||||||
background: "red",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
axios
|
|
||||||
.delete(
|
|
||||||
`${getBaseURL()}/api/sponsors/delete/${
|
|
||||||
sponsor.ID
|
|
||||||
}`,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${localStorage.getItem(
|
|
||||||
"token"
|
|
||||||
)}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
setSponsors(
|
|
||||||
sponsors.filter((s) => s.ID !== sponsor.ID)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DomainDisabledRounded />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Sponsoren;
|
|
|
@ -1,15 +1,9 @@
|
||||||
declare namespace Types {
|
declare namespace Types {
|
||||||
interface Article {
|
interface Worker {
|
||||||
ID: string;
|
Vorname: string;
|
||||||
title: string;
|
Nachname: string;
|
||||||
views: number;
|
Geburtstag: Date;
|
||||||
author: {
|
Anstelldatum: Date;
|
||||||
ID: string;
|
|
||||||
username: string;
|
|
||||||
}
|
|
||||||
sponsors: Sponsor[];
|
|
||||||
updatedAt: Date;
|
|
||||||
createdAt: Date;
|
|
||||||
}
|
}
|
||||||
interface User extends UserPermissions {
|
interface User extends UserPermissions {
|
||||||
ID: string;
|
ID: string;
|
||||||
|
|
|
@ -0,0 +1,657 @@
|
||||||
|
{
|
||||||
|
"name": "manager",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "manager",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/prompts": "^3.3.0",
|
||||||
|
"chalk": "^3.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/inquirer": "^9.0.7",
|
||||||
|
"@types/node": "^20.11.5",
|
||||||
|
"typescript": "^5.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/checkbox": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-3cKJkW1vIZAs4NaS0reFsnpAjP0azffYII4I2R7PTI7ZTMg5Y1at4vzXccOH3762b2c2L4drBhpJpf9uiaGNxA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/core": "^5.1.1",
|
||||||
|
"@inquirer/type": "^1.1.5",
|
||||||
|
"ansi-escapes": "^4.3.2",
|
||||||
|
"chalk": "^4.1.2",
|
||||||
|
"figures": "^3.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/checkbox/node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"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/@inquirer/confirm": {
|
||||||
|
"version": "2.0.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-2.0.15.tgz",
|
||||||
|
"integrity": "sha512-hj8Q/z7sQXsF0DSpLQZVDhWYGN6KLM/gNjjqGkpKwBzljbQofGjn0ueHADy4HUY+OqDHmXuwk/bY+tZyIuuB0w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/core": "^5.1.1",
|
||||||
|
"@inquirer/type": "^1.1.5",
|
||||||
|
"chalk": "^4.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/confirm/node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"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/@inquirer/core": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-IuJyZQUg75+L5AmopgnzxYrgcU6PJKL0hoIs332G1Gv55CnmZrhG6BzNOeZ5sOsTi1YCGOopw4rYICv74ejMFg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/type": "^1.1.5",
|
||||||
|
"@types/mute-stream": "^0.0.4",
|
||||||
|
"@types/node": "^20.9.0",
|
||||||
|
"@types/wrap-ansi": "^3.0.0",
|
||||||
|
"ansi-escapes": "^4.3.2",
|
||||||
|
"chalk": "^4.1.2",
|
||||||
|
"cli-spinners": "^2.9.1",
|
||||||
|
"cli-width": "^4.1.0",
|
||||||
|
"figures": "^3.2.0",
|
||||||
|
"mute-stream": "^1.0.0",
|
||||||
|
"run-async": "^3.0.0",
|
||||||
|
"signal-exit": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.1",
|
||||||
|
"wrap-ansi": "^6.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/core/node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"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/@inquirer/editor": {
|
||||||
|
"version": "1.2.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-1.2.13.tgz",
|
||||||
|
"integrity": "sha512-gBxjqt0B9GLN0j6M/tkEcmcIvB2fo9Cw0f5NRqDTkYyB9AaCzj7qvgG0onQ3GVPbMyMbbP4tWYxrBOaOdKpzNA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/core": "^5.1.1",
|
||||||
|
"@inquirer/type": "^1.1.5",
|
||||||
|
"chalk": "^4.1.2",
|
||||||
|
"external-editor": "^3.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/editor/node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"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/@inquirer/expand": {
|
||||||
|
"version": "1.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-1.1.14.tgz",
|
||||||
|
"integrity": "sha512-yS6fJ8jZYAsxdxuw2c8XTFMTvMR1NxZAw3LxDaFnqh7BZ++wTQ6rSp/2gGJhMacdZ85osb+tHxjVgx7F+ilv5g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/core": "^5.1.1",
|
||||||
|
"@inquirer/type": "^1.1.5",
|
||||||
|
"chalk": "^4.1.2",
|
||||||
|
"figures": "^3.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/expand/node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"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/@inquirer/input": {
|
||||||
|
"version": "1.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/input/-/input-1.2.14.tgz",
|
||||||
|
"integrity": "sha512-tISLGpUKXixIQue7jypNEShrdzJoLvEvZOJ4QRsw5XTfrIYfoWFqAjMQLerGs9CzR86yAI89JR6snHmKwnNddw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/core": "^5.1.1",
|
||||||
|
"@inquirer/type": "^1.1.5",
|
||||||
|
"chalk": "^4.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/input/node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"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/@inquirer/password": {
|
||||||
|
"version": "1.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/password/-/password-1.1.14.tgz",
|
||||||
|
"integrity": "sha512-vL2BFxfMo8EvuGuZYlryiyAB3XsgtbxOcFs4H9WI9szAS/VZCAwdVqs8rqEeaAf/GV/eZOghIOYxvD91IsRWSg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/input": "^1.2.14",
|
||||||
|
"@inquirer/type": "^1.1.5",
|
||||||
|
"ansi-escapes": "^4.3.2",
|
||||||
|
"chalk": "^4.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/password/node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"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/@inquirer/prompts": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-BBCqdSnhNs+WziSIo4f/RNDu6HAj4R/Q5nMgJb5MNPFX8sJGCvj9BoALdmR0HTWXyDS7TO8euKj6W6vtqCQG7A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/checkbox": "^1.5.0",
|
||||||
|
"@inquirer/confirm": "^2.0.15",
|
||||||
|
"@inquirer/core": "^5.1.1",
|
||||||
|
"@inquirer/editor": "^1.2.13",
|
||||||
|
"@inquirer/expand": "^1.1.14",
|
||||||
|
"@inquirer/input": "^1.2.14",
|
||||||
|
"@inquirer/password": "^1.1.14",
|
||||||
|
"@inquirer/rawlist": "^1.2.14",
|
||||||
|
"@inquirer/select": "^1.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/rawlist": {
|
||||||
|
"version": "1.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-1.2.14.tgz",
|
||||||
|
"integrity": "sha512-xIYmDpYgfz2XGCKubSDLKEvadkIZAKbehHdWF082AyC2I4eHK44RUfXaoOAqnbqItZq4KHXS6jDJ78F2BmQvxg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/core": "^5.1.1",
|
||||||
|
"@inquirer/type": "^1.1.5",
|
||||||
|
"chalk": "^4.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/rawlist/node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"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/@inquirer/select": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/select/-/select-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-EgOPHv7XOHEqiBwBJTyiMg9r57ySyW4oyYCumGp+pGyOaXQaLb2kTnccWI6NFd9HSi5kDJhF7YjA+3RfMQJ2JQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/core": "^5.1.1",
|
||||||
|
"@inquirer/type": "^1.1.5",
|
||||||
|
"ansi-escapes": "^4.3.2",
|
||||||
|
"chalk": "^4.1.2",
|
||||||
|
"figures": "^3.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/select/node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"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/@inquirer/type": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-wmwHvHozpPo4IZkkNtbYenem/0wnfI6hvOcGKmPEa0DwuaH5XUQzFqy6OpEpjEegZMhYIk8HDYITI16BPLtrRA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/inquirer": {
|
||||||
|
"version": "9.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.7.tgz",
|
||||||
|
"integrity": "sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/through": "*",
|
||||||
|
"rxjs": "^7.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/mute-stream": {
|
||||||
|
"version": "0.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz",
|
||||||
|
"integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "20.11.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz",
|
||||||
|
"integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/through": {
|
||||||
|
"version": "0.0.33",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz",
|
||||||
|
"integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/wrap-ansi": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g=="
|
||||||
|
},
|
||||||
|
"node_modules/ansi-escapes": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"type-fest": "^0.21.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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/chalk": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chardet": {
|
||||||
|
"version": "0.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
|
||||||
|
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
|
||||||
|
},
|
||||||
|
"node_modules/cli-spinners": {
|
||||||
|
"version": "2.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
|
||||||
|
"integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cli-width": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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/emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||||
|
},
|
||||||
|
"node_modules/escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/external-editor": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
|
||||||
|
"dependencies": {
|
||||||
|
"chardet": "^0.7.0",
|
||||||
|
"iconv-lite": "^0.4.24",
|
||||||
|
"tmp": "^0.0.33"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/figures": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
|
||||||
|
"dependencies": {
|
||||||
|
"escape-string-regexp": "^1.0.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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/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/is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mute-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==",
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/os-tmpdir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/run-async": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rxjs": {
|
||||||
|
"version": "7.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||||
|
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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/signal-exit": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=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/tmp": {
|
||||||
|
"version": "0.0.33",
|
||||||
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||||
|
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
|
||||||
|
"dependencies": {
|
||||||
|
"os-tmpdir": "~1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||||
|
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/type-fest": {
|
||||||
|
"version": "0.21.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
|
||||||
|
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||||
|
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||||
|
},
|
||||||
|
"node_modules/wrap-ansi": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "manager",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "npx tsc && node ."
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/inquirer": "^9.0.7",
|
||||||
|
"@types/node": "^20.11.5",
|
||||||
|
"typescript": "^5.3.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/prompts": "^3.3.0",
|
||||||
|
"chalk": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import chalk from "chalk";
|
||||||
|
|
||||||
|
export default class LoadSpinner {
|
||||||
|
spinner: string[] = [
|
||||||
|
'⠋',
|
||||||
|
'⠙',
|
||||||
|
'⠹',
|
||||||
|
'⠸',
|
||||||
|
'⠼',
|
||||||
|
'⠴',
|
||||||
|
'⠦',
|
||||||
|
'⠧',
|
||||||
|
'⠇',
|
||||||
|
'⠏'
|
||||||
|
];
|
||||||
|
spinnerIndex: number;
|
||||||
|
updateInterval: NodeJS.Timeout;
|
||||||
|
message: string;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(message: string) {
|
||||||
|
this.spinnerIndex = 0;
|
||||||
|
this.message = message;
|
||||||
|
|
||||||
|
// The spinner is a loop that runs every 100ms
|
||||||
|
// the spinner should look like this: [(spinner) message]
|
||||||
|
|
||||||
|
this.updateInterval = setInterval(() => {
|
||||||
|
this.updateSpinner();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateSpinner() {
|
||||||
|
process.stdout.write(`\r${this.spinner[this.spinnerIndex]} ${this.message}`);
|
||||||
|
this.spinnerIndex++;
|
||||||
|
if(this.spinnerIndex >= this.spinner.length) this.spinnerIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
changeMessage(message: string) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSpinner(finishMessage?: string) {
|
||||||
|
clearInterval(this.updateInterval);
|
||||||
|
// replace the spinner with a tick but keep the message
|
||||||
|
process.stdout.write(`\r${chalk.green("✓")} ${finishMessage ?? this.message}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
failSpinner(finishMessage?: string) {
|
||||||
|
clearInterval(this.updateInterval);
|
||||||
|
// replace the spinner with a cross but keep the message
|
||||||
|
process.stdout.write(`\r${chalk.red("✗")} ${finishMessage ?? this.message}\n`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { select, confirm } from '@inquirer/prompts';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
|
||||||
|
const configExists = fs.existsSync("../terratec.config.json");
|
||||||
|
|
||||||
|
|
||||||
|
async function MainMenu() {
|
||||||
|
if (!configExists) {
|
||||||
|
console.log(chalk.red(`No terratec.config.json found`));
|
||||||
|
|
||||||
|
const action = await confirm({
|
||||||
|
message: 'Do you want to create a new config file?',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (action) return await require('./modes/ConfigCreate').default();
|
||||||
|
else process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const action = await select({
|
||||||
|
message: 'Select Terra manager action',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'Actions',
|
||||||
|
value: 'action',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Edit config',
|
||||||
|
value: 'edit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Exit',
|
||||||
|
value: 'exit',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case 'action':
|
||||||
|
{
|
||||||
|
const selectedAction = await select({
|
||||||
|
message: 'Select Terra manager action',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'Start Dev Server',
|
||||||
|
value: 'dev',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Start Build',
|
||||||
|
value: 'build',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Exit',
|
||||||
|
value: 'exit',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
switch (selectedAction) {
|
||||||
|
case 'dev':
|
||||||
|
await require('./modes/Dev').default();
|
||||||
|
break;
|
||||||
|
case 'build':
|
||||||
|
await require('./modes/Build').default();
|
||||||
|
break;
|
||||||
|
case 'exit':
|
||||||
|
return MainMenu();
|
||||||
|
default:
|
||||||
|
console.log(chalk.red(`Unknown action: ${action}`));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'edit':
|
||||||
|
await require('./modes/ConfigEdit').default();
|
||||||
|
break;
|
||||||
|
case 'exit':
|
||||||
|
process.exit(0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log(chalk.red(`Unknown action: ${action}`));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
MainMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
MainMenu();
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { input } from "@inquirer/prompts";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import os from "os";
|
||||||
|
import fs from "fs";
|
||||||
|
import LoadSpinner from "../components/LoadSpinner";
|
||||||
|
|
||||||
|
export default async () => {
|
||||||
|
const name = (await input({
|
||||||
|
message: 'Project name',
|
||||||
|
default: 'my-app',
|
||||||
|
transformer: (input: string) => {
|
||||||
|
return input.toLowerCase().replace(/ /g, '-');
|
||||||
|
},
|
||||||
|
validate: (input: string) => {
|
||||||
|
if(input.length === 0) return 'Project name cannot be empty';
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
})).toLowerCase().replace(/ /g, '-');
|
||||||
|
const description = await input({
|
||||||
|
message: 'Project description',
|
||||||
|
default: 'My awesome app',
|
||||||
|
});
|
||||||
|
const author = await input({
|
||||||
|
message: 'Author',
|
||||||
|
default: os.userInfo().username,
|
||||||
|
});
|
||||||
|
const version = "0.0.1";
|
||||||
|
|
||||||
|
const saveSpinner = new LoadSpinner("Saving config...");
|
||||||
|
|
||||||
|
const config: Terratec.Config = {
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
version,
|
||||||
|
author,
|
||||||
|
project: []
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.writeFileSync("../terratec.config.json", JSON.stringify(config, null, 2));
|
||||||
|
|
||||||
|
saveSpinner.removeSpinner("Saved config!");
|
||||||
|
};
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { input, select } from "@inquirer/prompts";
|
||||||
|
import chalk from "chalk";
|
||||||
|
|
||||||
|
export default async () => {
|
||||||
|
const action = await select({
|
||||||
|
message: 'Select Terra manager action',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'Create new part',
|
||||||
|
value: 'create',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Delete part',
|
||||||
|
value: 'delete',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Exit',
|
||||||
|
value: 'exit',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case 'create':
|
||||||
|
await require('./PartCreate').default();
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
await require('./PartDelete').default();
|
||||||
|
break;
|
||||||
|
case 'exit':
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
console.log(chalk.red(`Unknown action: ${action}`));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { exec } from "child_process";
|
||||||
|
|
||||||
|
export default async () => {
|
||||||
|
console.log(chalk.green("Running dev mode"));
|
||||||
|
|
||||||
|
const frontend = exec("npm run start", {
|
||||||
|
cwd: "../frontend",
|
||||||
|
});
|
||||||
|
|
||||||
|
const backend = exec("npm run start", {
|
||||||
|
cwd: "../backend",
|
||||||
|
});
|
||||||
|
|
||||||
|
frontend.stdout?.on("data", (data) => {
|
||||||
|
console.log(chalk.blueBright("[Frontend]"), data);
|
||||||
|
});
|
||||||
|
|
||||||
|
backend.stdout?.on("data", (data) => {
|
||||||
|
console.log(chalk.blueBright("[Backend]"), data);
|
||||||
|
});
|
||||||
|
|
||||||
|
frontend.stderr?.on("data", (data) => {
|
||||||
|
console.log(chalk.redBright("[Frontend]"), data);
|
||||||
|
});
|
||||||
|
|
||||||
|
backend.stderr?.on("data", (data) => {
|
||||||
|
console.log(chalk.redBright("[Backend]"), data);
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,76 @@
|
||||||
|
import { input, select } from "@inquirer/prompts";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import fs from "fs";
|
||||||
|
import LoadSpinner from "../components/LoadSpinner";
|
||||||
|
|
||||||
|
const configTxt = fs.readFileSync("../terratec.config.json").toString();
|
||||||
|
const config: Terratec.Config = JSON.parse(configTxt);
|
||||||
|
|
||||||
|
const takenNames = config.project.map((project) => project.name);
|
||||||
|
|
||||||
|
export default async () => {
|
||||||
|
console.log(chalk.green("Config generator"));
|
||||||
|
|
||||||
|
const type = await select({
|
||||||
|
message: 'Part type',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'Frontend',
|
||||||
|
value: 'frontend',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Backend',
|
||||||
|
value: 'backend',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const name = (await input({
|
||||||
|
message: 'Part name',
|
||||||
|
transformer: (input: string) => {
|
||||||
|
return input.toLowerCase().replace(/ /g, '-');
|
||||||
|
},
|
||||||
|
validate: (input: string) => {
|
||||||
|
if (input.length === 0) return 'Part name cannot be empty';
|
||||||
|
if (takenNames.includes(input)) return 'Part name already taken';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})).toLowerCase().replace(/ /g, '-');
|
||||||
|
const description = await input({
|
||||||
|
message: 'Part description',
|
||||||
|
default: 'My awesome part',
|
||||||
|
});
|
||||||
|
|
||||||
|
const readTemplatesSpinner = new LoadSpinner("Reading templates...");
|
||||||
|
|
||||||
|
const templates = fs.readdirSync(`./dist/templates/${type}`);
|
||||||
|
|
||||||
|
readTemplatesSpinner.removeSpinner("Read templates!");
|
||||||
|
|
||||||
|
const template = await select({
|
||||||
|
message: 'Part template',
|
||||||
|
choices: [
|
||||||
|
...templates.map((template) => ({
|
||||||
|
name: template,
|
||||||
|
value: template,
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const saveSpinner = new LoadSpinner("Saving config...");
|
||||||
|
|
||||||
|
const config: Terratec.Config = JSON.parse(fs.readFileSync("../terratec.config.json").toString());
|
||||||
|
|
||||||
|
config.project.push({
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
type: type as Terratec.ProjectType,
|
||||||
|
template,
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.writeFileSync("../terratec.config.json", JSON.stringify(config, null, 2));
|
||||||
|
|
||||||
|
saveSpinner.removeSpinner("Saved config!");
|
||||||
|
|
||||||
|
await require(`../templates/${type}/${template}`).default(config.project[config.project.length - 1]);
|
||||||
|
};
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { input, select, confirm } from "@inquirer/prompts";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import fs from "fs";
|
||||||
|
import LoadSpinner from "../components/LoadSpinner";
|
||||||
|
|
||||||
|
export default async () => {
|
||||||
|
console.log(chalk.green("Config generator"));
|
||||||
|
|
||||||
|
const configTxT = fs.readFileSync("../terratec.config.json").toString();
|
||||||
|
const config: Terratec.Config = JSON.parse(configTxT);
|
||||||
|
|
||||||
|
const part = await select({
|
||||||
|
message: 'Delete part',
|
||||||
|
choices: config.project.map((project) => (
|
||||||
|
{
|
||||||
|
name: project.name,
|
||||||
|
value: project.name,
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
|
||||||
|
const sure = await confirm({
|
||||||
|
message: `Are you sure you want to delete ${part}? All files will be deleted!`,
|
||||||
|
default: false,
|
||||||
|
});
|
||||||
|
if (!sure) return console.log(chalk.red("Aborted"));
|
||||||
|
|
||||||
|
const saveSpinner = new LoadSpinner("Saving config...");
|
||||||
|
|
||||||
|
config.project = config.project.filter((project) => project.name !== part);
|
||||||
|
|
||||||
|
fs.writeFileSync("../terratec.config.json", JSON.stringify(config, null, 2));
|
||||||
|
|
||||||
|
saveSpinner.removeSpinner("Saved config!");
|
||||||
|
|
||||||
|
const deleteSpinner = new LoadSpinner("Removing files...");
|
||||||
|
|
||||||
|
fs.rmSync(`../${part}`, { recursive: true });
|
||||||
|
|
||||||
|
deleteSpinner.removeSpinner("Files removed!");
|
||||||
|
};
|
|
@ -0,0 +1,79 @@
|
||||||
|
import { input, confirm, select, checkbox } from "@inquirer/prompts"
|
||||||
|
import { exec } from "child_process"
|
||||||
|
import LoadSpinner from "../../components/LoadSpinner"
|
||||||
|
import chalk from "chalk"
|
||||||
|
import fs from "fs"
|
||||||
|
|
||||||
|
export default async (props: Terratec.ProjectConfig) => {
|
||||||
|
console.log(chalk.green("Running template..."))
|
||||||
|
|
||||||
|
const typescript = await confirm({
|
||||||
|
message: "Use typescript?",
|
||||||
|
default: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const spinner = new LoadSpinner("Running template...");
|
||||||
|
fs.mkdirSync(`../${props.name}`);
|
||||||
|
|
||||||
|
spinner.changeMessage("Setting up package...")
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
exec(`npm init -y`, {
|
||||||
|
cwd: `../${props.name}`,
|
||||||
|
}, () => {
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
fs.mkdirSync(`../${props.name}/src`);
|
||||||
|
|
||||||
|
if(typescript) {
|
||||||
|
spinner.changeMessage("Installing typescript...")
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
exec(`npm i typescript @types/node @types/express --save-dev`, {
|
||||||
|
cwd: `../${props.name}`,
|
||||||
|
}, () => {
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
spinner.changeMessage("Creating tsconfig.json...")
|
||||||
|
|
||||||
|
fs.writeFileSync(`../${props.name}/tsconfig.json`, JSON.stringify({
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es6",
|
||||||
|
"module": "commonjs",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true
|
||||||
|
}
|
||||||
|
}, null, 2));
|
||||||
|
|
||||||
|
fs.writeFileSync(`../${props.name}/src/index.ts`, `console.log("Hello world!")`);
|
||||||
|
} else {
|
||||||
|
fs.writeFileSync(`../${props.name}/src/index.js`, `console.log("Hello world!")`);
|
||||||
|
}
|
||||||
|
|
||||||
|
spinner.changeMessage("Updating package.json...")
|
||||||
|
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync(`../${props.name}/package.json`).toString());
|
||||||
|
packageJson.main = typescript ? "dist/index.js" : "src/index.js";
|
||||||
|
packageJson.scripts.start = typescript ? "npx tsc && node ." : "node src/index.js";
|
||||||
|
|
||||||
|
fs.writeFileSync(`../${props.name}/package.json`, JSON.stringify(packageJson, null, 2));
|
||||||
|
|
||||||
|
spinner.changeMessage("Installing dependencies...")
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
exec(`npm i express`, {
|
||||||
|
cwd: `../${props.name}`,
|
||||||
|
}, () => {
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
spinner.removeSpinner("Template execution finished!");
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { input, confirm, select, checkbox } from "@inquirer/prompts"
|
||||||
|
import { exec } from "child_process"
|
||||||
|
import LoadSpinner from "../../components/LoadSpinner"
|
||||||
|
import chalk from "chalk"
|
||||||
|
|
||||||
|
export default async (props: Terratec.ProjectConfig) => {
|
||||||
|
console.log(chalk.green("Running template..."))
|
||||||
|
|
||||||
|
const typescript = await confirm({
|
||||||
|
message: "Use typescript?",
|
||||||
|
default: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const spinner = new LoadSpinner("Running template...");
|
||||||
|
spinner.changeMessage("Running create-react-app...")
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
exec(`npx create-react-app ${props.name}${typescript && " --template typescript"}`, {
|
||||||
|
cwd: "..",
|
||||||
|
}, () => {
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
spinner.removeSpinner("Template execution finished!");
|
||||||
|
|
||||||
|
const optionDependencies = await confirm({
|
||||||
|
message: "Do you want to install optional dependencies?",
|
||||||
|
default: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if(optionDependencies) {
|
||||||
|
const dependencies = await checkbox({
|
||||||
|
message: "Select dependencies",
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: "react-router-dom",
|
||||||
|
value: "react-router-dom",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "axios",
|
||||||
|
value: "axios",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "react-query",
|
||||||
|
value: "react-query",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Material UI",
|
||||||
|
value: "@mui/icons-material @mui/material @emotion/styled @emotion/react",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Zustand",
|
||||||
|
value: "zustand",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const dependenciesSpinner = new LoadSpinner("Installing dependencies...");
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
exec(`npm i ${dependencies.join(" ")}`, {
|
||||||
|
cwd: `../${props.name}`,
|
||||||
|
}, () => {
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
dependenciesSpinner.removeSpinner("Dependencies installed!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
declare namespace Terratec {
|
||||||
|
type ProjectType = 'frontend' | 'backend';
|
||||||
|
|
||||||
|
interface Config {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
version: string;
|
||||||
|
author: string;
|
||||||
|
project: ProjectConfig[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProjectConfig {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
type: ProjectType;
|
||||||
|
template: 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,7 @@
|
||||||
|
{
|
||||||
|
"name": "my-app",
|
||||||
|
"description": "My awesome app",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"author": "niko",
|
||||||
|
"project": []
|
||||||
|
}
|