Gay ass
This commit is contained in:
parent
57a1a0105f
commit
64ea467e3c
@ -9,14 +9,14 @@ datasource db {
|
||||
extensions = [uuid_ossp(map: "uuid-ossp")]
|
||||
}
|
||||
|
||||
model users {
|
||||
ID String @id @default(dbgenerated("uuid_generate_v1()")) @db.Uuid
|
||||
model Users {
|
||||
ID String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
|
||||
username String
|
||||
password String @db.VarChar(128)
|
||||
token String @db.VarChar(128)
|
||||
}
|
||||
|
||||
model Mitarbeiter {
|
||||
model Employees {
|
||||
ID String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
|
||||
Vorname String
|
||||
Nachname String
|
||||
|
@ -62,7 +62,7 @@ app.get('/api/auth/verify', async (req, res) => {
|
||||
res.send('OK')
|
||||
})
|
||||
|
||||
app.post("/api/worker/create", async (req, res) => {
|
||||
app.put("/api/employees", async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
if (!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
@ -70,13 +70,43 @@ app.post("/api/worker/create", async (req, res) => {
|
||||
const user = await authorize(token)
|
||||
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const { title, content, image, sponsors } = req.body
|
||||
if (!title || !content || !image) return res.status(400).send(Errors.MISSING_ITEMS)
|
||||
if (!Array.isArray(req.body)) return res.status(400).send(Errors.INVALID_ITEMS)
|
||||
|
||||
// sponsors is an array of sponsor IDs
|
||||
const employees: {
|
||||
ID: string;
|
||||
Vorname: string;
|
||||
Nachname: string;
|
||||
Geburtstag?: number | Date;
|
||||
Anstelldatum?: number | Date;
|
||||
}[] = req.body;
|
||||
|
||||
for (let i in employees) {
|
||||
if (
|
||||
!employees[i].Vorname
|
||||
|| !employees[i].Nachname
|
||||
|| employees[i].ID
|
||||
) return res.status(400).send(Errors.MISSING_ITEMS)
|
||||
|
||||
if (employees[i].Geburtstag && typeof employees[i].Geburtstag !== 'number') return res.status(400).send(Errors.INVALID_ITEMS)
|
||||
if (employees[i].Anstelldatum && typeof employees[i].Anstelldatum !== 'number') return res.status(400).send(Errors.INVALID_ITEMS)
|
||||
|
||||
if (employees[i].Geburtstag && typeof employees[i].Geburtstag === 'number') employees[i].Geburtstag = new Date(employees[i].Geburtstag as number)
|
||||
if (employees[i].Anstelldatum && typeof employees[i].Anstelldatum === 'number') employees[i].Anstelldatum = new Date(employees[i].Anstelldatum as number)
|
||||
}
|
||||
|
||||
const newEmployees = await prisma.employees.createMany({
|
||||
data: employees as {
|
||||
Vorname: string;
|
||||
Nachname: string;
|
||||
Geburtstag?: Date;
|
||||
Anstelldatum?: Date;
|
||||
}[]
|
||||
})
|
||||
|
||||
res.send(newEmployees)
|
||||
})
|
||||
|
||||
app.get('/api/workers', async (req, res) => {
|
||||
app.patch("/api/employees/:id", async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
if (!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
@ -84,18 +114,128 @@ app.get('/api/workers', async (req, res) => {
|
||||
const user = await authorize(token)
|
||||
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const workers = await prisma.mitarbeiter.findMany({
|
||||
let { Vorname, Nachname, Geburtstag, Anstelldatum } = req.body as {
|
||||
Vorname: string;
|
||||
Nachname: string;
|
||||
Geburtstag?: number | Date;
|
||||
Anstelldatum?: number | Date;
|
||||
}
|
||||
if (!Vorname || !Nachname) return res.status(400).send(Errors.MISSING_ITEMS)
|
||||
|
||||
|
||||
if (Geburtstag && typeof Geburtstag !== 'number') return res.status(400).send(Errors.INVALID_ITEMS)
|
||||
if (Anstelldatum && typeof Anstelldatum !== 'number') return res.status(400).send(Errors.INVALID_ITEMS)
|
||||
|
||||
if (Geburtstag && typeof Geburtstag === 'number') Geburtstag = new Date(Geburtstag)
|
||||
if (Anstelldatum && typeof Anstelldatum === 'number') Anstelldatum = new Date(Anstelldatum)
|
||||
|
||||
const updatedEmployee = await prisma.employees.update({
|
||||
where: {
|
||||
ID: req.params.id
|
||||
},
|
||||
data: {
|
||||
Vorname,
|
||||
Nachname,
|
||||
Geburtstag: (Geburtstag as Date | undefined),
|
||||
Anstelldatum: (Anstelldatum as Date | undefined)
|
||||
}
|
||||
})
|
||||
|
||||
res.send(updatedEmployee)
|
||||
})
|
||||
|
||||
app.patch("/api/employees/", async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
if (!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
const user = await authorize(token)
|
||||
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
if (!Array.isArray(req.body)) return res.status(400).send(Errors.INVALID_ITEMS)
|
||||
|
||||
const employees: {
|
||||
ID: string;
|
||||
Vorname: string;
|
||||
Nachname: string;
|
||||
Geburtstag?: number | Date;
|
||||
Anstelldatum?: number | Date;
|
||||
}[] = req.body;
|
||||
|
||||
for (let i in employees) {
|
||||
if (
|
||||
!employees[i].Vorname
|
||||
|| !employees[i].Nachname
|
||||
|| !employees[i].ID
|
||||
) return res.status(400).send(Errors.MISSING_ITEMS)
|
||||
|
||||
if (employees[i].Geburtstag && typeof employees[i].Geburtstag !== 'number') return res.status(400).send(Errors.INVALID_ITEMS)
|
||||
if (employees[i].Anstelldatum && typeof employees[i].Anstelldatum !== 'number') return res.status(400).send(Errors.INVALID_ITEMS)
|
||||
|
||||
if (employees[i].Geburtstag && typeof employees[i].Geburtstag === 'number') employees[i].Geburtstag = new Date(employees[i].Geburtstag as number)
|
||||
if (employees[i].Anstelldatum && typeof employees[i].Anstelldatum === 'number') employees[i].Anstelldatum = new Date(employees[i].Anstelldatum as number)
|
||||
}
|
||||
|
||||
const updatedEmployees = await Promise.all(employees.map(employee =>
|
||||
prisma.employees.update({
|
||||
where: { ID: employee.ID },
|
||||
data: {
|
||||
Vorname: employee.Vorname,
|
||||
Nachname: employee.Nachname,
|
||||
Geburtstag: employee.Geburtstag as Date | undefined,
|
||||
Anstelldatum: employee.Anstelldatum as Date | undefined
|
||||
}
|
||||
})
|
||||
))
|
||||
|
||||
res.send(updatedEmployees)
|
||||
})
|
||||
|
||||
app.get('/api/employees', 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 workers = await prisma.employees.findMany({
|
||||
select: {
|
||||
ID: true,
|
||||
Vorname: true,
|
||||
Nachname: true,
|
||||
Geburtstag: true,
|
||||
Anstelldatum: true,
|
||||
},
|
||||
orderBy: {
|
||||
Nachname: 'asc'
|
||||
}
|
||||
})
|
||||
|
||||
res.send(workers)
|
||||
})
|
||||
|
||||
app.delete('/api/employees/:employees', 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 employees: string[] = req.params.employees.split(',')
|
||||
|
||||
await prisma.employees.deleteMany({
|
||||
where: {
|
||||
ID: {
|
||||
in: employees
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
res.send('OK')
|
||||
})
|
||||
|
||||
app.get('/api/users', async (req, res) => {
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
|
||||
@ -172,8 +312,8 @@ app.patch('/api/users/edit/:id', async (req, res) => {
|
||||
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
|
||||
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)
|
||||
const { username, password } = req.body
|
||||
if (!username) return res.status(400).send(Errors.MISSING_ITEMS)
|
||||
|
||||
const newUser = await prisma.users.update({
|
||||
where: {
|
||||
@ -181,11 +321,6 @@ app.patch('/api/users/edit/:id', async (req, res) => {
|
||||
},
|
||||
data: {
|
||||
username,
|
||||
admin,
|
||||
article_create,
|
||||
article_manage,
|
||||
sponsor_manage,
|
||||
user_manage,
|
||||
...(password && { password })
|
||||
}
|
||||
})
|
||||
@ -211,65 +346,56 @@ app.delete('/api/users/delete/:id', async (req, res) => {
|
||||
|
||||
|
||||
|
||||
|
||||
app.get('/api/toshow/:targetDate?', async (req, res) => {
|
||||
const { targetDate } = req.params;
|
||||
if(targetDate && targetDate.length !== 10) return res.status(400).send(Errors.INVALID_ITEMS)
|
||||
const token = req.headers.authorization?.split(' ')[1]
|
||||
if (targetDate && targetDate.length !== 10) return res.status(400).send(Errors.INVALID_ITEMS)
|
||||
|
||||
if (!token) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
let dates: Types.EmployeeEvent[] = await prisma.employees.findMany({})
|
||||
|
||||
const user = await authorize(token)
|
||||
if (!user) return res.status(401).send(Errors.INVALID_CREDENTIALS)
|
||||
|
||||
let dates: Types.Mitarbeiter[] = await prisma.mitarbeiter.findMany({})
|
||||
|
||||
let currDate;
|
||||
if(targetDate) currDate = moment(targetDate, "YYYY-MM-DD");
|
||||
let currDate;
|
||||
if (targetDate) currDate = moment(targetDate, "YYYY-MM-DD");
|
||||
else currDate = moment();
|
||||
for (let i in dates) {
|
||||
let validity = false;
|
||||
|
||||
|
||||
// `${currDate.getFullYear()}-${getFormatNumber(date.getMonth(), 2)}-${getFormatNumber(date.getDay(), 2)}`
|
||||
|
||||
|
||||
if (dates[i].Anstelldatum) {
|
||||
const date = dates[i].Anstelldatum;
|
||||
const dateWithoutYear = moment(date).format("MM-DD");
|
||||
|
||||
if (
|
||||
date &&
|
||||
date &&
|
||||
(
|
||||
(
|
||||
moment(dateWithoutYear,"MM-DD") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear,"MM-DD") <= currDate
|
||||
)
|
||||
moment(dateWithoutYear, "MM-DD") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear, "MM-DD") <= currDate
|
||||
)
|
||||
||
|
||||
(
|
||||
moment(dateWithoutYear,"MM-DD").subtract(1, "year") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear,"MM-DD").subtract(1, "year") <= currDate
|
||||
moment(dateWithoutYear, "MM-DD").subtract(1, "year") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear, "MM-DD").subtract(1, "year") <= currDate
|
||||
)
|
||||
)
|
||||
) {
|
||||
validity = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (dates[i].Geburtstag) {
|
||||
const date = dates[i].Geburtstag;
|
||||
const dateWithoutYear = moment(date).format("MM-DD");
|
||||
|
||||
if (
|
||||
date &&
|
||||
date &&
|
||||
(
|
||||
(
|
||||
moment(dateWithoutYear,"MM-DD") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear,"MM-DD") <= currDate
|
||||
)
|
||||
moment(dateWithoutYear, "MM-DD") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear, "MM-DD") <= currDate
|
||||
)
|
||||
||
|
||||
(
|
||||
moment(dateWithoutYear,"MM-DD").subtract(1, "year") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear,"MM-DD").subtract(1, "year") <= currDate
|
||||
moment(dateWithoutYear, "MM-DD").subtract(1, "year") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear, "MM-DD").subtract(1, "year") <= currDate
|
||||
)
|
||||
)
|
||||
) {
|
||||
@ -277,36 +403,36 @@ app.get('/api/toshow/:targetDate?', async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!validity) delete dates[i];
|
||||
}
|
||||
dates = dates.filter((date) => date !== null);
|
||||
|
||||
|
||||
for(let i = 14; i >= 1; i--) {
|
||||
|
||||
for (let i = 14; i >= 1; i--) {
|
||||
console.log(i);
|
||||
const date = moment(currDate).subtract(i, "days")
|
||||
const dayOfWeek = date.format("dddd");
|
||||
|
||||
if(["Saturday", "Sunday"].includes(dayOfWeek)) continue;
|
||||
if (["Saturday", "Sunday"].includes(dayOfWeek)) continue;
|
||||
|
||||
for(let j in dates) {
|
||||
for (let j in dates) {
|
||||
let validity = false;
|
||||
|
||||
if (dates[j].Anstelldatum) {
|
||||
const dateWithoutYear = moment(dates[j].Anstelldatum, "YYYY-MM-DD").format("MM-DD");
|
||||
|
||||
if (
|
||||
date &&
|
||||
date &&
|
||||
(
|
||||
(
|
||||
moment(dateWithoutYear,"MM-DD") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear,"MM-DD") <= moment(currDate).subtract(i, "days")
|
||||
)
|
||||
moment(dateWithoutYear, "MM-DD") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear, "MM-DD") <= moment(currDate).subtract(i, "days")
|
||||
)
|
||||
||
|
||||
(
|
||||
moment(dateWithoutYear,"MM-DD").subtract(1, "year") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear,"MM-DD").subtract(1, "year") <= moment(currDate).subtract(i, "days")
|
||||
moment(dateWithoutYear, "MM-DD").subtract(1, "year") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear, "MM-DD").subtract(1, "year") <= moment(currDate).subtract(i, "days")
|
||||
)
|
||||
)
|
||||
) {
|
||||
@ -318,16 +444,16 @@ app.get('/api/toshow/:targetDate?', async (req, res) => {
|
||||
const dateWithoutYear = moment(dates[j].Geburtstag, "YYYY-MM-DD").format("MM-DD");
|
||||
|
||||
if (
|
||||
date &&
|
||||
date &&
|
||||
(
|
||||
(
|
||||
moment(dateWithoutYear,"MM-DD") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear,"MM-DD") <= moment(currDate).subtract(i, "days")
|
||||
)
|
||||
moment(dateWithoutYear, "MM-DD") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear, "MM-DD") <= moment(currDate).subtract(i, "days")
|
||||
)
|
||||
||
|
||||
(
|
||||
moment(dateWithoutYear,"MM-DD").subtract(1, "year") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear,"MM-DD").subtract(1, "year") <= moment(currDate).subtract(i, "days")
|
||||
moment(dateWithoutYear, "MM-DD").subtract(1, "year") >= moment(currDate).subtract(14, "days") &&
|
||||
moment(dateWithoutYear, "MM-DD").subtract(1, "year") <= moment(currDate).subtract(i, "days")
|
||||
)
|
||||
)
|
||||
) {
|
||||
@ -337,9 +463,26 @@ app.get('/api/toshow/:targetDate?', async (req, res) => {
|
||||
|
||||
|
||||
console.log(date);
|
||||
console.log(date.format("dddd"), dates[j].Vorname, dates[j].Nachname, validity);
|
||||
|
||||
if (validity) delete dates[j];
|
||||
if (validity) {
|
||||
delete dates[j];
|
||||
continue;
|
||||
}
|
||||
|
||||
// get the EventType depending on whats closer to the current date
|
||||
const dateWithoutYear = moment(dates[j].Anstelldatum, "YYYY-MM-DD").format("MM-DD");
|
||||
const dateWithoutYear2 = moment(dates[j].Geburtstag, "YYYY-MM-DD").format("MM-DD");
|
||||
|
||||
if (dates[j].Anstelldatum && !dates[j].Geburtstag) dates[j].EventType = "Jubiläum";
|
||||
else if (dates[j].Geburtstag && !dates[j].Anstelldatum) dates[j].EventType = "Geburtstag";
|
||||
else if (
|
||||
Math.abs(moment(dateWithoutYear, "MM-DD").diff(moment(currDate), "days")) <=
|
||||
Math.abs(moment(dateWithoutYear2, "MM-DD").diff(moment(currDate), "days"))
|
||||
) dates[j].EventType = "Jubiläum"
|
||||
else dates[j].EventType = "Geburtstag"
|
||||
|
||||
|
||||
console.log(date.format("dddd"), dates[j].Vorname, dates[j].Nachname, validity, dates[j].EventType);
|
||||
}
|
||||
dates = dates.filter((date) => date !== null);
|
||||
}
|
||||
|
6
backend/src/types.d.ts
vendored
6
backend/src/types.d.ts
vendored
@ -5,11 +5,15 @@ declare namespace Types {
|
||||
message: string
|
||||
}
|
||||
|
||||
interface Mitarbeiter {
|
||||
interface Employees {
|
||||
ID: string;
|
||||
Vorname: string;
|
||||
Nachname: string;
|
||||
Anstelldatum: Date | null;
|
||||
Geburtstag: Date | null;
|
||||
}
|
||||
|
||||
interface EmployeeEvent extends Employees {
|
||||
EventType?: "Geburtstag" | "Jubiläum";
|
||||
}
|
||||
}
|
179
frontend/package-lock.json
generated
179
frontend/package-lock.json
generated
@ -16,12 +16,14 @@
|
||||
"@mui/lab": "^5.0.0-alpha.154",
|
||||
"@mui/material": "^5.14.10",
|
||||
"@mui/x-data-grid": "^6.19.1",
|
||||
"@mui/x-date-pickers": "^6.19.6",
|
||||
"@types/node": "^16.18.52",
|
||||
"@types/react": "^18.2.22",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"axios": "^1.5.1",
|
||||
"js-sha256": "^0.10.1",
|
||||
"moment": "^2.29.4",
|
||||
"papaparse": "^5.4.1",
|
||||
"react": "^18.2.0",
|
||||
"react-calendar": "^4.8.0",
|
||||
"react-dom": "^18.2.0",
|
||||
@ -32,6 +34,9 @@
|
||||
"suneditor-react": "^3.6.1",
|
||||
"typescript": "^4.9.5",
|
||||
"zustand": "^4.4.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/papaparse": "^5.3.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@aashutoshrathi/word-wrap": {
|
||||
@ -1951,9 +1956,9 @@
|
||||
"integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.23.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.5.tgz",
|
||||
"integrity": "sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==",
|
||||
"version": "7.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz",
|
||||
"integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
@ -2528,28 +2533,28 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz",
|
||||
"integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz",
|
||||
"integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.1.3"
|
||||
"@floating-ui/utils": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz",
|
||||
"integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==",
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz",
|
||||
"integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.4.2",
|
||||
"@floating-ui/utils": "^0.1.3"
|
||||
"@floating-ui/core": "^1.0.0",
|
||||
"@floating-ui/utils": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react-dom": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz",
|
||||
"integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==",
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz",
|
||||
"integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.5.1"
|
||||
"@floating-ui/dom": "^1.6.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
@ -2557,9 +2562,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.4.tgz",
|
||||
"integrity": "sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA=="
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz",
|
||||
"integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
|
||||
},
|
||||
"node_modules/@fontsource-variable/lexend": {
|
||||
"version": "5.0.12",
|
||||
@ -3603,9 +3608,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/types": {
|
||||
"version": "7.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.10.tgz",
|
||||
"integrity": "sha512-wX1vbDC+lzF7FlhT6A3ffRZgEoKWPF8VqRoTu4lZwouFX2t90KyCMsgepMw5DxLak1BSp/KP86CmtZttikb/gQ==",
|
||||
"version": "7.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.13.tgz",
|
||||
"integrity": "sha512-qP9OgacN62s+l8rdDhSFRe05HWtLLJ5TGclC9I1+tQngbssu0m2dmFZs+Px53AcOs9fD7TbYd4gc9AXzVqO/+g==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0"
|
||||
},
|
||||
@ -3616,11 +3621,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/utils": {
|
||||
"version": "5.14.19",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.19.tgz",
|
||||
"integrity": "sha512-qAHvTXzk7basbyqPvhgWqN6JbmI2wLB/mf97GkSlz5c76MiKYV6Ffjvw9BjKZQ1YRb8rDX9kgdjRezOcoB91oQ==",
|
||||
"version": "5.15.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.12.tgz",
|
||||
"integrity": "sha512-8SDGCnO2DY9Yy+5bGzu00NZowSDtuyHP4H8gunhHGQoIlhlY2Z3w64wBzAOLpYw/ZhJNzksDTnS/i8qdJvxuow==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.4",
|
||||
"@babel/runtime": "^7.23.9",
|
||||
"@types/prop-types": "^15.7.11",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^18.2.0"
|
||||
@ -3672,6 +3677,102 @@
|
||||
"react-dom": "^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/x-date-pickers": {
|
||||
"version": "6.19.6",
|
||||
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.19.6.tgz",
|
||||
"integrity": "sha512-QW9AFcPi0vLpkUhmquhhyhLaBvB0AZJuu3NTrE173qNKx3Z3n51aCLY9bc7c6i4ltZMMsVRHlvzQjsve04TC8A==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.2",
|
||||
"@mui/base": "^5.0.0-beta.22",
|
||||
"@mui/utils": "^5.14.16",
|
||||
"@types/react-transition-group": "^4.4.8",
|
||||
"clsx": "^2.0.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-transition-group": "^4.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.9.0",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"@mui/material": "^5.8.6",
|
||||
"@mui/system": "^5.8.0",
|
||||
"date-fns": "^2.25.0 || ^3.2.0",
|
||||
"date-fns-jalali": "^2.13.0-0",
|
||||
"dayjs": "^1.10.7",
|
||||
"luxon": "^3.0.2",
|
||||
"moment": "^2.29.4",
|
||||
"moment-hijri": "^2.1.2",
|
||||
"moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
|
||||
"react": "^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
},
|
||||
"date-fns": {
|
||||
"optional": true
|
||||
},
|
||||
"date-fns-jalali": {
|
||||
"optional": true
|
||||
},
|
||||
"dayjs": {
|
||||
"optional": true
|
||||
},
|
||||
"luxon": {
|
||||
"optional": true
|
||||
},
|
||||
"moment": {
|
||||
"optional": true
|
||||
},
|
||||
"moment-hijri": {
|
||||
"optional": true
|
||||
},
|
||||
"moment-jalaali": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/x-date-pickers/node_modules/@mui/base": {
|
||||
"version": "5.0.0-beta.38",
|
||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.38.tgz",
|
||||
"integrity": "sha512-AsjD6Y1X5A1qndxz8xCcR8LDqv31aiwlgWMPxFAX/kCKiIGKlK65yMeVZ62iQr/6LBz+9hSKLiD1i4TZdAHKcQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.9",
|
||||
"@floating-ui/react-dom": "^2.0.8",
|
||||
"@mui/types": "^7.2.13",
|
||||
"@mui/utils": "^5.15.12",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"clsx": "^2.1.0",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
|
||||
"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",
|
||||
@ -4328,6 +4429,15 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.52.tgz",
|
||||
"integrity": "sha512-sm2aph6cRSsTMFYFgI+RpPLunXO9ClJkpizUVdT7KmGeyfQ14xnjTMT/f3MHcfKqevXqGT6BgVFzW8wcEoDUtA=="
|
||||
},
|
||||
"node_modules/@types/papaparse": {
|
||||
"version": "5.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.14.tgz",
|
||||
"integrity": "sha512-LxJ4iEFcpqc6METwp9f6BV6VVc43m6MfH0VqFosHvrUgfXiFe6ww7R3itkOQ+TCK6Y+Iv/+RnnvtRZnkc5Kc9g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||
@ -4377,9 +4487,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-transition-group": {
|
||||
"version": "4.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz",
|
||||
"integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==",
|
||||
"version": "4.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz",
|
||||
"integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==",
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@ -6042,9 +6152,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
|
||||
"integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz",
|
||||
"integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@ -12842,6 +12952,11 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/papaparse": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz",
|
||||
"integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw=="
|
||||
},
|
||||
"node_modules/param-case": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
||||
|
@ -11,12 +11,14 @@
|
||||
"@mui/lab": "^5.0.0-alpha.154",
|
||||
"@mui/material": "^5.14.10",
|
||||
"@mui/x-data-grid": "^6.19.1",
|
||||
"@mui/x-date-pickers": "^6.19.6",
|
||||
"@types/node": "^16.18.52",
|
||||
"@types/react": "^18.2.22",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"axios": "^1.5.1",
|
||||
"js-sha256": "^0.10.1",
|
||||
"moment": "^2.29.4",
|
||||
"papaparse": "^5.4.1",
|
||||
"react": "^18.2.0",
|
||||
"react-calendar": "^4.8.0",
|
||||
"react-dom": "^18.2.0",
|
||||
@ -50,5 +52,8 @@
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/papaparse": "^5.3.14"
|
||||
}
|
||||
}
|
||||
|
@ -46,8 +46,8 @@ function Sidebar() {
|
||||
gap: "2px",
|
||||
}}>
|
||||
<SidebarElement Title="Dashboard" Icon={Home} Path="/admin/dashboard" />
|
||||
<SidebarElement Title="Daten import" Icon={TableChartOutlined} Path="/admin/import" />
|
||||
<SidebarElement Title="Kalender" Icon={Event} Path="/admin/Kalender" />
|
||||
<SidebarElement Title="Verwalten" Icon={TableChartOutlined} Path="/admin/manage" />
|
||||
{/* <SidebarElement Title="Kalender" Icon={Event} Path="/admin/Kalender" /> */}
|
||||
<SidebarElement Title="Benutzer" Icon={Person} Path="/admin/benutzer" />
|
||||
|
||||
<SidebarElement Title="Zur Website" Icon={ArrowBack} Path="/" sx={{
|
||||
|
@ -29,14 +29,6 @@ function UserModal({
|
||||
const [username, setUsername] = useState<string>("");
|
||||
const [password, setPassword] = useState<string>("");
|
||||
|
||||
const [permissions, setPermissions] = useState<Types.UserPermissions>({
|
||||
admin: false,
|
||||
article_create: false,
|
||||
article_manage: false,
|
||||
sponsor_manage: false,
|
||||
user_manage: false,
|
||||
});
|
||||
|
||||
const valid = () => {
|
||||
if (!username) return false;
|
||||
if (!password && id === "create") return false;
|
||||
@ -47,13 +39,6 @@ function UserModal({
|
||||
useEffect(() => {
|
||||
setUsername("");
|
||||
setPassword("");
|
||||
setPermissions({
|
||||
admin: false,
|
||||
article_create: false,
|
||||
article_manage: false,
|
||||
sponsor_manage: false,
|
||||
user_manage: false,
|
||||
});
|
||||
|
||||
if (!id) return;
|
||||
if (id === "create") return;
|
||||
@ -67,15 +52,6 @@ function UserModal({
|
||||
})
|
||||
.then((res) => {
|
||||
setUsername(res.data.username);
|
||||
|
||||
setPermissions({
|
||||
admin: res.data.admin,
|
||||
article_create: res.data.article_create,
|
||||
article_manage: res.data.article_manage,
|
||||
sponsor_manage: res.data.sponsor_manage,
|
||||
user_manage: res.data.user_manage,
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
});
|
||||
}, [id]);
|
||||
@ -186,112 +162,6 @@ function UserModal({
|
||||
}}
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
|
||||
width: "80%",
|
||||
mx: "20px",
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={1}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Checkbox
|
||||
checked={permissions.admin}
|
||||
onChange={(e) => {
|
||||
setPermissions({
|
||||
...permissions,
|
||||
admin: e.target.checked,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
Admin
|
||||
</Stack>
|
||||
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={1}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Checkbox
|
||||
disabled={permissions.admin}
|
||||
checked={permissions.article_create}
|
||||
onChange={(e) => {
|
||||
setPermissions({
|
||||
...permissions,
|
||||
article_create: e.target.checked,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
Artikel Erstellen
|
||||
</Stack>
|
||||
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={1}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Checkbox
|
||||
disabled={permissions.admin}
|
||||
checked={permissions.article_manage}
|
||||
onChange={(e) => {
|
||||
setPermissions({
|
||||
...permissions,
|
||||
article_manage: e.target.checked,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
Artikel Verwalten
|
||||
</Stack>
|
||||
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={1}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Checkbox
|
||||
disabled={permissions.admin}
|
||||
checked={permissions.sponsor_manage}
|
||||
onChange={(e) => {
|
||||
setPermissions({
|
||||
...permissions,
|
||||
sponsor_manage: e.target.checked,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
Sponsoren Verwalten
|
||||
</Stack>
|
||||
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={1}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Checkbox
|
||||
disabled={permissions.admin}
|
||||
checked={permissions.user_manage}
|
||||
onChange={(e) => {
|
||||
setPermissions({
|
||||
...permissions,
|
||||
user_manage: e.target.checked,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
Nutzer Verwalten
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
sx={{ width: "80%" }}
|
||||
@ -305,20 +175,7 @@ function UserModal({
|
||||
`${getBaseURL()}/api/users/create`,
|
||||
{
|
||||
username,
|
||||
password: sha256(`rheine ${password.trim()} rheine`),
|
||||
admin: permissions.admin,
|
||||
article_create: permissions.admin
|
||||
? false
|
||||
: permissions.article_create,
|
||||
article_manage: permissions.admin
|
||||
? false
|
||||
: permissions.article_manage,
|
||||
sponsor_manage: permissions.admin
|
||||
? false
|
||||
: permissions.sponsor_manage,
|
||||
user_manage: permissions.admin
|
||||
? false
|
||||
: permissions.user_manage,
|
||||
password: sha256(`rheine ${password.trim()} rheine`)
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
@ -339,20 +196,7 @@ function UserModal({
|
||||
username,
|
||||
password: password
|
||||
? sha256(`rheine ${password.trim()} rheine`)
|
||||
: undefined,
|
||||
admin: permissions.admin,
|
||||
article_create: permissions.admin
|
||||
? false
|
||||
: permissions.article_create,
|
||||
article_manage: permissions.admin
|
||||
? false
|
||||
: permissions.article_manage,
|
||||
sponsor_manage: permissions.admin
|
||||
? false
|
||||
: permissions.sponsor_manage,
|
||||
user_manage: permissions.admin
|
||||
? false
|
||||
: permissions.user_manage,
|
||||
: undefined
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
export function getBaseURL(): string {
|
||||
if(process.env.NODE_ENV === "development") return "http://100.108.94.138:3001"
|
||||
if(process.env.NODE_ENV === "development") return "http://127.0.0.1:3001"
|
||||
else return ""
|
||||
}
|
||||
|
||||
|
@ -3,12 +3,16 @@ import "./index.css";
|
||||
import App from "./App";
|
||||
import { createTheme, ThemeProvider, CssBaseline } from "@mui/material";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment';
|
||||
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||
import moment from "moment";
|
||||
|
||||
// fonts
|
||||
import '@fontsource-variable/overpass';
|
||||
import '@fontsource-variable/lexend';
|
||||
|
||||
moment.locale("de");
|
||||
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
mode: "light",
|
||||
@ -41,7 +45,9 @@ root.render(
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<BrowserRouter>
|
||||
<LocalizationProvider dateAdapter={AdapterMoment}>
|
||||
<App />
|
||||
</LocalizationProvider>
|
||||
</BrowserRouter>
|
||||
</ThemeProvider>
|
||||
);
|
@ -4,8 +4,8 @@ import Sidebar from "../components/Sidebar";
|
||||
import Dashboard from "./admin/Dashboard";
|
||||
import Benutzer from "./admin/Benutzer";
|
||||
import Logout from "./admin/Logout";
|
||||
import Import from "./admin/Import";
|
||||
import Kalender from "./admin/Kalender";
|
||||
import ManageData from "./admin/ManageData";
|
||||
|
||||
function AdminFrame() {
|
||||
return (
|
||||
@ -31,7 +31,7 @@ function AdminFrame() {
|
||||
<Route path="/dashboard" element={<Dashboard />} />
|
||||
<Route path="/benutzer" element={<Benutzer />} />
|
||||
<Route path="/logout" element={<Logout />} />
|
||||
<Route path="/import" element={<Import />} />
|
||||
<Route path="/manage" element={<ManageData />} />
|
||||
<Route path="/kalender" element={<Kalender />} />
|
||||
</Routes>
|
||||
</Box>
|
||||
|
@ -1,21 +1,52 @@
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { Box, Collapse, Typography } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import PhilSwift from "../components/PhilSwift";
|
||||
import axios from "axios";
|
||||
import { getBaseURL } from "../functions";
|
||||
import CompareDate from "../components/CompareDate";
|
||||
import moment from "moment";
|
||||
|
||||
interface EmployeeWithColor extends Types.Employees {
|
||||
color: string;
|
||||
EventType: "Geburtstag" | "Jubiläum";
|
||||
}
|
||||
|
||||
function LandingPage() {
|
||||
const [isHalloween, setIsHalloween] = useState(false);
|
||||
const [data, setData] = useState<EmployeeWithColor[]>([]);
|
||||
const [currEvent, setCurrEvent] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
const currentDate = new Date();
|
||||
const halloweenDate = new Date("2024-03-04");
|
||||
axios
|
||||
.get(`${getBaseURL()}/api/toshow`)
|
||||
.then((res) => {
|
||||
setData(res.data.map((employee: Types.Employees) => ({
|
||||
...employee,
|
||||
color: `hsl(${Math.random() * 360}, 100%, 50%)`,
|
||||
})));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}, []);
|
||||
|
||||
setIsHalloween(
|
||||
currentDate.getMonth() === halloweenDate.getMonth() &&
|
||||
currentDate.getDate() === halloweenDate.getDate()
|
||||
);
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setCurrEvent((prev) => {
|
||||
if (data.length === 0) return 0;
|
||||
if (data.length === 1) return 0;
|
||||
if (prev === data.length - 1) return 0;
|
||||
else return prev + 1;
|
||||
});
|
||||
}, 5000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
window.location.reload();
|
||||
}, moment().endOf("day").diff(moment(), "milliseconds"));
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@ -32,7 +63,7 @@ function LandingPage() {
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<PhilSwift />
|
||||
{/* <PhilSwift /> */}
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
@ -48,20 +79,52 @@ function LandingPage() {
|
||||
zIndex: 10,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "2rem",
|
||||
color: "red",
|
||||
textAlign: "center",
|
||||
fontWeight: "bold",
|
||||
textShadow: "2px 2px 4px #000000",
|
||||
zIndex: 10,
|
||||
}}
|
||||
>
|
||||
{isHalloween
|
||||
? "Happy Halloween"
|
||||
: "Keine Geburtstage oder Jubiläen heute."}
|
||||
</Typography>
|
||||
{data.map((employee, index) => {
|
||||
return (
|
||||
<Collapse
|
||||
in={currEvent === index}
|
||||
key={index}
|
||||
orientation="horizontal"
|
||||
timeout={1000} // Add this line to make the collapse slower
|
||||
easing={"ease-in-out"}
|
||||
>
|
||||
<Box sx={{
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
}}>
|
||||
<Typography
|
||||
variant="h1"
|
||||
sx={{
|
||||
width: "100vw",
|
||||
textAlign: "center",
|
||||
fontFamily: "Lexend Variable",
|
||||
color: employee.color,
|
||||
fontSize: "15rem",
|
||||
}}
|
||||
>
|
||||
{employee.Vorname} {employee.Nachname}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h2"
|
||||
sx={{
|
||||
width: "100vw",
|
||||
textAlign: "center",
|
||||
fontFamily: "Lexend Variable",
|
||||
color: employee.color,
|
||||
fontSize: "5rem",
|
||||
}}
|
||||
>
|
||||
{employee.EventType === "Geburtstag" ? "hat Geburtstag" : "hat Jubiläum"}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Collapse>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
@ -112,7 +112,6 @@ function Benutzer() {
|
||||
<TableCell align="left" width="700px">
|
||||
Name
|
||||
</TableCell>
|
||||
<TableCell align="right">Berechtigungen</TableCell>
|
||||
<TableCell align="right"></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
@ -120,18 +119,6 @@ function Benutzer() {
|
||||
{users.map((user) => (
|
||||
<TableRow>
|
||||
<TableCell align="left">{user.username}</TableCell>
|
||||
<TableCell align="right">
|
||||
{[
|
||||
{ name: "Admin", value: user.admin },
|
||||
{ name: "Artikel Erstellen", value: user.article_create },
|
||||
{ name: "Artikel Verwalten", value: user.article_manage },
|
||||
{ name: "Sponsoren Verwalten", value: user.sponsor_manage },
|
||||
{ name: "Nutzer Verwalten", value: user.user_manage },
|
||||
]
|
||||
.filter((e) => e.value)
|
||||
.map((e) => e.name)
|
||||
.join(", ")}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Box
|
||||
sx={{
|
||||
|
@ -1,115 +1,18 @@
|
||||
import { Visibility, Newspaper, Person, Savings } from "@mui/icons-material";
|
||||
import { Box, Grid, SvgIconTypeMap, Typography } from "@mui/material";
|
||||
import { OverridableComponent } from "@mui/material/OverridableComponent";
|
||||
import axios from "axios";
|
||||
import { useEffect, useState } from "react";
|
||||
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")
|
||||
|
||||
},
|
||||
];
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import React from 'react'
|
||||
|
||||
function Dashboard() {
|
||||
const [workers, setWorkers] = useState<Types.Worker[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
axios
|
||||
.get(`${getBaseURL()}/api/workers`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
},
|
||||
})
|
||||
.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 (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
|
||||
pt: "30px",
|
||||
px: "30px",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h4" sx={{ fontWeight: "bold" }}>
|
||||
Nächste Geburtstage
|
||||
</Typography>
|
||||
|
||||
<DataGrid
|
||||
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 sx={{
|
||||
width: "100%",
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}>
|
||||
<Typography variant="h1">Noch nicht Implementiert!</Typography>
|
||||
</Box>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export default Dashboard;
|
||||
export default Dashboard
|
899
frontend/src/pages/admin/ManageData.tsx
Normal file
899
frontend/src/pages/admin/ManageData.tsx
Normal file
@ -0,0 +1,899 @@
|
||||
import {
|
||||
Alert,
|
||||
Backdrop,
|
||||
Box,
|
||||
Button,
|
||||
Checkbox,
|
||||
CircularProgress,
|
||||
Collapse,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TextField,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import axios from "axios";
|
||||
import { useEffect, useState } from "react";
|
||||
import { getBaseURL } from "../../functions";
|
||||
import moment from "moment";
|
||||
import { Add, Delete, Edit, Refresh, UploadFile } from "@mui/icons-material";
|
||||
import { DatePicker } from "@mui/x-date-pickers";
|
||||
import PapaParse from "papaparse";
|
||||
|
||||
function ManageData() {
|
||||
const [employees, setEmployees] = useState<Types.Employees[]>([]);
|
||||
const [checked, setChecked] = useState<string[]>([]);
|
||||
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
const [createPopup, setCreatePopup] = useState<boolean>(false);
|
||||
const [editPopup, setEditPopup] = useState<Types.Employees | false>(false);
|
||||
const [deletePopup, setDeletePopup] = useState<Types.Employees | false>(
|
||||
false
|
||||
);
|
||||
const [uploadPopup, setUploadPopup] = useState<boolean>(false);
|
||||
|
||||
const getEmployees = () => {
|
||||
setLoading(true);
|
||||
setChecked([]);
|
||||
axios
|
||||
.get(`${getBaseURL()}/api/employees`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
setEmployees(res.data);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getEmployees();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!createPopup) getEmployees();
|
||||
}, [createPopup]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editPopup) getEmployees();
|
||||
}, [editPopup]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!deletePopup) getEmployees();
|
||||
}, [deletePopup]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!uploadPopup) getEmployees();
|
||||
}, [uploadPopup]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
|
||||
pt: "30px",
|
||||
px: "30px",
|
||||
}}
|
||||
>
|
||||
<Backdrop open={loading} sx={{ zIndex: 1000 }}>
|
||||
<CircularProgress color="secondary" />
|
||||
</Backdrop>
|
||||
|
||||
{CreatePopUp(createPopup, setCreatePopup)}
|
||||
{EditPopUp(editPopup, setEditPopup)}
|
||||
{DeletePopUp(deletePopup, setDeletePopup)}
|
||||
{UploadPopUp(uploadPopup, setUploadPopup)}
|
||||
|
||||
<Typography variant="h4" sx={{ fontWeight: "bold" }}>
|
||||
Daten Verwalten
|
||||
</Typography>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "flex-end",
|
||||
padding: "10px",
|
||||
backgroundColor: "#00000022",
|
||||
borderRadius: "5px",
|
||||
ml: "auto",
|
||||
}}
|
||||
>
|
||||
<Tooltip arrow placement="top" title="Mitarbeiter Hinzufügen">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCreatePopup(true);
|
||||
}}
|
||||
>
|
||||
<Add />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip arrow placement="top" title="Daten Aktualisieren">
|
||||
<Button onClick={() => getEmployees()}>
|
||||
<Refresh />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip arrow placement="top" title="CSV Hochladen">
|
||||
<Button onClick={() => setUploadPopup(true)}>
|
||||
<UploadFile />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Collapse orientation="horizontal" in={checked.length > 0}>
|
||||
<Tooltip arrow placement="top" title="Ausgewählte Löschen">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
axios
|
||||
.delete(
|
||||
`${getBaseURL()}/api/employees/${checked.join(",")}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem(
|
||||
"token"
|
||||
)}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
getEmployees();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Collapse>
|
||||
</Box>
|
||||
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={checked.length === employees.length}
|
||||
onChange={(_, v) => {
|
||||
if (v) setChecked(employees.map((employee) => employee.ID));
|
||||
else setChecked([]);
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>Nachname</TableCell>
|
||||
<TableCell>Vorname</TableCell>
|
||||
<TableCell>Geburtstag</TableCell>
|
||||
<TableCell>Anstelldatum</TableCell>
|
||||
<TableCell align="right"></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{employees.map((employee, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={checked.includes(employee.ID)}
|
||||
onChange={(_, v) => {
|
||||
if (v) setChecked([...checked, employee.ID]);
|
||||
else setChecked(checked.filter((id) => id !== employee.ID));
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>{employee.Nachname}</TableCell>
|
||||
<TableCell>{employee.Vorname}</TableCell>
|
||||
<TableCell>
|
||||
{employee.Geburtstag
|
||||
? moment(employee.Geburtstag).format("DD.MM.YYYY")
|
||||
: "-"}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{employee.Anstelldatum
|
||||
? moment(employee.Anstelldatum).format("DD.MM.YYYY")
|
||||
: "-"}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
setEditPopup(employee);
|
||||
}}
|
||||
>
|
||||
<Edit />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="error"
|
||||
onClick={() => {
|
||||
setDeletePopup(employee);
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
</Button>
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default ManageData;
|
||||
|
||||
function CreatePopUp(
|
||||
createPopup: boolean,
|
||||
setCreatePopup: (value: boolean) => void
|
||||
) {
|
||||
const [vorname, setVorname] = useState<string>("");
|
||||
const [nachname, setNachname] = useState<string>("");
|
||||
|
||||
const [showGeburtstag, setShowGeburtstag] = useState<boolean>(false);
|
||||
const [geburtstag, setGeburtstag] = useState<moment.Moment | null>(null);
|
||||
const [showAnstelldatum, setShowAnstelldatum] = useState<boolean>(false);
|
||||
const [anstelldatum, setAnstelldatum] = useState<moment.Moment | null>(null);
|
||||
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
const checkValid = () => {
|
||||
if (vorname === "" || vorname.length > 24) return false;
|
||||
if (nachname === "" || nachname.length > 24) return false;
|
||||
|
||||
if (showGeburtstag && !geburtstag) return false;
|
||||
if (showAnstelldatum && !anstelldatum) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!createPopup) {
|
||||
setVorname("");
|
||||
setNachname("");
|
||||
setShowGeburtstag(false);
|
||||
setGeburtstag(null);
|
||||
setShowAnstelldatum(false);
|
||||
setAnstelldatum(null);
|
||||
setLoading(false);
|
||||
}
|
||||
}, [createPopup]);
|
||||
|
||||
return (
|
||||
<Backdrop open={createPopup} sx={{ zIndex: 1000 }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: "500px",
|
||||
height: "auto",
|
||||
backgroundColor: "white",
|
||||
borderRadius: "5px",
|
||||
boxShadow: "0 0 10px #00000055",
|
||||
padding: "20px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontWeight: "bold", fontSize: "30px" }}>
|
||||
Neuen Mitarbeiter Hinzufügen
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
label="Vorname"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
value={vorname}
|
||||
maxRows={1}
|
||||
onChange={(e) => {
|
||||
if (e.target.value.length > 24) return;
|
||||
setVorname(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
label="Nachname"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
value={nachname}
|
||||
maxRows={1}
|
||||
onChange={(e) => {
|
||||
if (e.target.value.length > 24) return;
|
||||
setNachname(e.target.value);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={showGeburtstag}
|
||||
onChange={(_, v) => setShowGeburtstag(v)}
|
||||
/>
|
||||
<DatePicker
|
||||
label="Geburtstag"
|
||||
sx={{ width: "100%" }}
|
||||
format="DD.MM.YYYY"
|
||||
value={geburtstag}
|
||||
onChange={(v) => setGeburtstag(v)}
|
||||
disabled={!showGeburtstag}
|
||||
maxDate={moment() as any}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={showAnstelldatum}
|
||||
onChange={(_, v) => setShowAnstelldatum(v)}
|
||||
/>
|
||||
<DatePicker
|
||||
label="Anstelldatum"
|
||||
sx={{ width: "100%" }}
|
||||
format="DD.MM.YYYY"
|
||||
value={anstelldatum}
|
||||
onChange={(v) => setAnstelldatum(v)}
|
||||
disabled={!showAnstelldatum}
|
||||
maxDate={moment() as any}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "flex-end",
|
||||
padding: "10px",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={!checkValid() || loading}
|
||||
onClick={() => {
|
||||
if (!checkValid()) return;
|
||||
setLoading(true);
|
||||
axios
|
||||
.put(
|
||||
`${getBaseURL()}/api/employees`,
|
||||
[
|
||||
{
|
||||
Vorname: vorname,
|
||||
Nachname: nachname,
|
||||
Geburtstag: showGeburtstag
|
||||
? moment(geburtstag).add(1, "day").toDate().getTime()
|
||||
: null,
|
||||
Anstelldatum: showAnstelldatum
|
||||
? moment(anstelldatum).add(1, "day").toDate().getTime()
|
||||
: null,
|
||||
},
|
||||
],
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
setCreatePopup(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
setLoading(false);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{loading && <CircularProgress size={20} />} Hinzufügen
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setCreatePopup(false);
|
||||
}}
|
||||
>
|
||||
Abbrechen
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Backdrop>
|
||||
);
|
||||
}
|
||||
|
||||
function EditPopUp(
|
||||
editPopup: Types.Employees | false,
|
||||
setEditPopup: (value: Types.Employees | false) => void
|
||||
) {
|
||||
const [vorname, setVorname] = useState<string>(
|
||||
editPopup ? editPopup.Vorname : ""
|
||||
);
|
||||
const [nachname, setNachname] = useState<string>(
|
||||
editPopup ? editPopup.Nachname : ""
|
||||
);
|
||||
|
||||
const [showGeburtstag, setShowGeburtstag] = useState<boolean>(
|
||||
editPopup ? !!editPopup.Geburtstag : false
|
||||
);
|
||||
const [geburtstag, setGeburtstag] = useState<moment.Moment | null>(
|
||||
editPopup
|
||||
? editPopup.Geburtstag
|
||||
? moment(editPopup.Geburtstag)
|
||||
: null
|
||||
: null
|
||||
);
|
||||
const [showAnstelldatum, setShowAnstelldatum] = useState<boolean>(
|
||||
editPopup ? !!editPopup.Anstelldatum : false
|
||||
);
|
||||
const [anstelldatum, setAnstelldatum] = useState<moment.Moment | null>(
|
||||
editPopup
|
||||
? editPopup.Anstelldatum
|
||||
? moment(editPopup.Anstelldatum)
|
||||
: null
|
||||
: null
|
||||
);
|
||||
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
const checkValid = () => {
|
||||
if (vorname === "" || vorname.length > 24) return false;
|
||||
if (nachname === "" || nachname.length > 24) return false;
|
||||
|
||||
if (showGeburtstag && !geburtstag) return false;
|
||||
if (showAnstelldatum && !anstelldatum) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (editPopup) {
|
||||
setVorname(editPopup.Vorname);
|
||||
setNachname(editPopup.Nachname);
|
||||
setShowGeburtstag(!!editPopup.Geburtstag);
|
||||
setGeburtstag(editPopup.Geburtstag ? moment(editPopup.Geburtstag) : null);
|
||||
setShowAnstelldatum(!!editPopup.Anstelldatum);
|
||||
setAnstelldatum(
|
||||
editPopup.Anstelldatum ? moment(editPopup.Anstelldatum) : null
|
||||
);
|
||||
} else {
|
||||
setVorname("");
|
||||
setNachname("");
|
||||
setShowGeburtstag(false);
|
||||
setGeburtstag(null);
|
||||
setShowAnstelldatum(false);
|
||||
setAnstelldatum(null);
|
||||
setLoading(false);
|
||||
}
|
||||
}, [editPopup]);
|
||||
|
||||
return (
|
||||
<Backdrop open={editPopup !== false} sx={{ zIndex: 1000 }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: "500px",
|
||||
height: "auto",
|
||||
backgroundColor: "white",
|
||||
borderRadius: "5px",
|
||||
boxShadow: "0 0 10px #00000055",
|
||||
padding: "20px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontWeight: "bold", fontSize: "30px" }}>
|
||||
{editPopup && editPopup.Vorname[0]}
|
||||
{". "} {editPopup && editPopup.Nachname} Bearbeiten
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
label="Vorname"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
value={vorname}
|
||||
maxRows={1}
|
||||
onChange={(e) => {
|
||||
if (e.target.value.length > 24) return;
|
||||
setVorname(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
label="Nachname"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
value={nachname}
|
||||
maxRows={1}
|
||||
onChange={(e) => {
|
||||
if (e.target.value.length > 24) return;
|
||||
setNachname(e.target.value);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={showGeburtstag}
|
||||
onChange={(_, v) => setShowGeburtstag(v)}
|
||||
/>
|
||||
<DatePicker
|
||||
label="Geburtstag"
|
||||
sx={{ width: "100%" }}
|
||||
format="DD.MM.YYYY"
|
||||
value={geburtstag}
|
||||
onChange={(v) => setGeburtstag(v)}
|
||||
disabled={!showGeburtstag}
|
||||
maxDate={moment() as any}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={showAnstelldatum}
|
||||
onChange={(_, v) => setShowAnstelldatum(v)}
|
||||
/>
|
||||
<DatePicker
|
||||
label="Anstelldatum"
|
||||
sx={{ width: "100%" }}
|
||||
format="DD.MM.YYYY"
|
||||
value={anstelldatum}
|
||||
onChange={(v) => setAnstelldatum(v)}
|
||||
disabled={!showAnstelldatum}
|
||||
maxDate={moment() as any}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "flex-end",
|
||||
padding: "10px",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={!checkValid() || loading}
|
||||
onClick={() => {
|
||||
if (!checkValid()) return;
|
||||
setLoading(true);
|
||||
if (editPopup && !editPopup.ID) return;
|
||||
axios
|
||||
.patch(
|
||||
`${getBaseURL()}/api/employees`,
|
||||
[
|
||||
{
|
||||
ID: editPopup && editPopup.ID,
|
||||
Vorname: vorname,
|
||||
Nachname: nachname,
|
||||
Geburtstag: showGeburtstag
|
||||
? editPopup && editPopup.Geburtstag
|
||||
? moment(geburtstag).toDate().getTime()
|
||||
: moment(geburtstag).add(1, "day").toDate().getTime()
|
||||
: null,
|
||||
Anstelldatum: showAnstelldatum
|
||||
? editPopup && editPopup.Anstelldatum
|
||||
? moment(anstelldatum).toDate().getTime()
|
||||
: moment(anstelldatum)
|
||||
.add(1, "day")
|
||||
.toDate()
|
||||
.getTime()
|
||||
: null,
|
||||
},
|
||||
],
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
setEditPopup(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
setLoading(false);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{loading && <CircularProgress size={20} />} Speichern
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setEditPopup(false);
|
||||
}}
|
||||
>
|
||||
Abbrechen
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Backdrop>
|
||||
);
|
||||
}
|
||||
|
||||
function DeletePopUp(
|
||||
deletePopup: Types.Employees | false,
|
||||
setDeletePopup: (value: Types.Employees | false) => void
|
||||
) {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!deletePopup) setLoading(false);
|
||||
}, [deletePopup]);
|
||||
|
||||
if (deletePopup === false) return <></>;
|
||||
|
||||
return (
|
||||
<Backdrop open={Boolean(deletePopup)} sx={{ zIndex: 1000 }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: "500px",
|
||||
height: "auto",
|
||||
backgroundColor: "white",
|
||||
borderRadius: "5px",
|
||||
boxShadow: "0 0 10px #00000055",
|
||||
padding: "20px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontWeight: "bold", fontSize: "30px" }}>
|
||||
Mitarbeiter Löschen
|
||||
</Typography>
|
||||
|
||||
<Typography>
|
||||
Wollen Sie wirklich den Mitarbeiter{" "}
|
||||
<strong>
|
||||
{deletePopup?.Vorname} {deletePopup?.Nachname}{" "}
|
||||
</strong>
|
||||
löschen?
|
||||
</Typography>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "flex-end",
|
||||
padding: "10px",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
if (deletePopup && !deletePopup.ID) return;
|
||||
axios
|
||||
.delete(`${getBaseURL()}/api/employees/${deletePopup?.ID}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
setDeletePopup(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
setLoading(false);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{loading && <CircularProgress size={20} />} Löschen
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setDeletePopup(false);
|
||||
}}
|
||||
>
|
||||
Abbrechen
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Backdrop>
|
||||
);
|
||||
}
|
||||
|
||||
interface EmployeePartial {
|
||||
Vorname: string;
|
||||
Nachname: string;
|
||||
Geburtstag: moment.Moment | null;
|
||||
Anstelldatum: moment.Moment | null;
|
||||
}
|
||||
|
||||
function UploadPopUp(
|
||||
uploadPopup: boolean,
|
||||
setUploadPopup: (value: boolean) => void
|
||||
) {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
const [data, setData] = useState<EmployeePartial[] | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!uploadPopup) setLoading(false);
|
||||
}, [uploadPopup]);
|
||||
|
||||
return (
|
||||
<Backdrop open={uploadPopup} sx={{ zIndex: 1000 }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: "500px",
|
||||
height: "auto",
|
||||
backgroundColor: "white",
|
||||
borderRadius: "5px",
|
||||
boxShadow: "0 0 10px #00000055",
|
||||
padding: "20px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontWeight: "bold", fontSize: "30px" }}>
|
||||
CSV Hochladen
|
||||
</Typography>
|
||||
|
||||
<Collapse in={error !== null}>
|
||||
<Alert severity="error">{error}</Alert>
|
||||
</Collapse>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
onChange={(e) => {
|
||||
if (!e.target.files) return;
|
||||
|
||||
const file = e.target.files[0];
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const result = e.target?.result as string;
|
||||
|
||||
const data: PapaParse.ParseResult<string[]> = PapaParse.parse(result)
|
||||
|
||||
if((data.data[0]).length !== 4) return setError("CSV Datei ist nicht im richtigen Format");
|
||||
if(data.data[0][0] !== "Vorname") return setError("CSV Datei ist nicht im richtigen Format");
|
||||
if(data.data[0][1] !== "Nachname") return setError("CSV Datei ist nicht im richtigen Format");
|
||||
if(data.data[0][2] !== "Geburtstag") return setError("CSV Datei ist nicht im richtigen Format");
|
||||
if(data.data[0][3] !== "Anstelldatum") return setError("CSV Datei ist nicht im richtigen Format");
|
||||
|
||||
const employees: EmployeePartial[] = [];
|
||||
for(let i = 1; i < data.data.length; i++) {
|
||||
const employee = data.data[i];
|
||||
if(employee.length !== 4) return setError("CSV Datei ist nicht im richtigen Format");
|
||||
if(employee[0].length > 24) return setError("Vorname ist zu lang");
|
||||
if(employee[1].length > 24) return setError("Nachname ist zu lang");
|
||||
if(employee[2] !== "" && !moment(employee[2], "DD.MM.YYYY", true).isValid()) return setError("Geburtstag ist nicht im richtigen Format");
|
||||
if(employee[3] !== "" && !moment(employee[3], "DD.MM.YYYY", true).isValid()) return setError("Anstelldatum ist nicht im richtigen Format");
|
||||
|
||||
employees.push({
|
||||
Vorname: employee[0],
|
||||
Nachname: employee[1],
|
||||
Geburtstag: employee[2] === "" ? null : moment(employee[2], "DD.MM.YYYY"),
|
||||
Anstelldatum: employee[3] === "" ? null : moment(employee[3], "DD.MM.YYYY"),
|
||||
});
|
||||
}
|
||||
|
||||
setData(employees);
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "flex-end",
|
||||
padding: "10px",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={loading || !data}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
axios.put(`${getBaseURL()}/api/employees`, data, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
{loading && <CircularProgress size={20} />} Hochladen
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setUploadPopup(false);
|
||||
}}
|
||||
>
|
||||
Abbrechen
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Backdrop>
|
||||
);
|
||||
}
|
15
frontend/src/types.d.ts
vendored
15
frontend/src/types.d.ts
vendored
@ -1,9 +1,10 @@
|
||||
declare namespace Types {
|
||||
interface Worker {
|
||||
interface Employees {
|
||||
ID: string;
|
||||
Vorname: string;
|
||||
Nachname: string;
|
||||
Geburtstag: Date;
|
||||
Anstelldatum: Date;
|
||||
Geburtstag?: Date;
|
||||
Anstelldatum?: Date;
|
||||
}
|
||||
interface User extends UserPermissions {
|
||||
ID: string;
|
||||
@ -17,12 +18,4 @@ declare namespace Types {
|
||||
sponsor_manage: boolean;
|
||||
user_manage: boolean;
|
||||
}
|
||||
|
||||
interface Sponsor {
|
||||
ID: string;
|
||||
name: string;
|
||||
url: string;
|
||||
description: string;
|
||||
addedAt: Date;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user