diff --git a/backend/package-lock.json b/backend/package-lock.json index 51fc668..c91bf99 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,6 +13,7 @@ "chalk": "^4.0.0", "dotenv": "^16.3.1", "express": "^4.18.2", + "moment": "^2.30.1", "prisma": "^5.3.1" }, "devDependencies": { @@ -569,6 +570,14 @@ "node": ">= 0.6" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", diff --git a/backend/package.json b/backend/package.json index 5cb630d..c34bf39 100644 --- a/backend/package.json +++ b/backend/package.json @@ -14,6 +14,7 @@ "chalk": "^4.0.0", "dotenv": "^16.3.1", "express": "^4.18.2", + "moment": "^2.30.1", "prisma": "^5.3.1" }, "devDependencies": { diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index eda633d..479962b 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -17,9 +17,9 @@ model users { } model Mitarbeiter { - ID String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + ID String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid Vorname String Nachname String - Anstelldatum DateTime @db.Date - Geburtstag DateTime @db.Date + Anstelldatum DateTime? @db.Date + Geburtstag DateTime? @db.Date } diff --git a/backend/src/functions.ts b/backend/src/functions.ts index bcb12ab..bb428f8 100644 --- a/backend/src/functions.ts +++ b/backend/src/functions.ts @@ -10,6 +10,7 @@ export function errorBuilder(type: string, message: string): Types.ErrorResponse const Errors = { MISSING_ITEMS: errorBuilder("missing_items", "Die Anfrage ist Fehlerhaft."), + INVALID_ITEMS: errorBuilder("invalid_items", "Die Anfrage ist Fehlerhaft."), INVALID_CREDENTIALS: errorBuilder("invalid_credentials", "Die eingegebenen Anmeldedaten sind inkorrekt!"), NOT_FOUND: errorBuilder("not_found", "Die angeforderte Ressource wurde nicht gefunden!"), } @@ -24,4 +25,9 @@ export async function authorize(token: string) { if (!user) return false return user +} + +export function getFormatNumber(number: number, digits: number): string { + // format the number with the digits. so if the digits is 2 and the number is 9 return 09 + return number.toString().padStart(digits, '0') } \ No newline at end of file diff --git a/backend/src/index.ts b/backend/src/index.ts index f684e4e..6179324 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -2,9 +2,10 @@ import { PrismaClient } from '@prisma/client' import express from 'express' import dotenv from 'dotenv' import init from './init' -import Errors, { authorize } from './functions' +import Errors, { authorize, getFormatNumber } from './functions' import fs from 'fs' import crypto from 'crypto' +import moment from 'moment' dotenv.config() @@ -210,6 +211,144 @@ 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 (!token) return res.status(401).send(Errors.INVALID_CREDENTIALS) + + 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"); + 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 && + ( + ( + 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 + ) + ) + ) { + validity = true; + } + } + + if (dates[i].Geburtstag) { + const date = dates[i].Geburtstag; + const dateWithoutYear = moment(date).format("MM-DD"); + + if ( + date && + ( + ( + 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 + ) + ) + ) { + validity = true; + } + } + + + if (!validity) delete dates[i]; + } + dates = dates.filter((date) => date !== null); + + + 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; + + 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 && + ( + ( + 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") + ) + ) + ) { + validity = true; + } + } + + if (dates[j].Geburtstag) { + const dateWithoutYear = moment(dates[j].Geburtstag, "YYYY-MM-DD").format("MM-DD"); + + if ( + date && + ( + ( + 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") + ) + ) + ) { + validity = true; + } + } + + + console.log(date); + console.log(date.format("dddd"), dates[j].Vorname, dates[j].Nachname, validity); + + if (validity) delete dates[j]; + } + dates = dates.filter((date) => date !== null); + } + + res.send(dates) +}) + + + app.listen(process.env.PORT, () => { console.log(`Server is running on port ${process.env.PORT}`) }) \ No newline at end of file diff --git a/backend/src/types.d.ts b/backend/src/types.d.ts index 402a4e8..5dcba8c 100644 --- a/backend/src/types.d.ts +++ b/backend/src/types.d.ts @@ -4,4 +4,12 @@ declare namespace Types { type: string message: string } + + interface Mitarbeiter { + ID: string; + Vorname: string; + Nachname: string; + Anstelldatum: Date | null; + Geburtstag: Date | null; + } } \ No newline at end of file diff --git a/frontend/src/components/CompareDate.tsx b/frontend/src/components/CompareDate.tsx new file mode 100644 index 0000000..85a3208 --- /dev/null +++ b/frontend/src/components/CompareDate.tsx @@ -0,0 +1,25 @@ +import axios from 'axios'; +import React, { useEffect, useState } from 'react'; +import { getBaseURL } from '../functions'; + +const CompareDate = () => { + axios + .get(`${getBaseURL()}/api/workers`, { + headers: { + Authorization: `Bearer ${localStorage.getItem("token")}`, + }, + }) + .then(response => { + console.log(response.data); + }) + .catch(error => { + console.error(error); + }); + return ( +
+

Compare Date

+
+ ); +}; + +export default CompareDate; \ No newline at end of file diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx index f658a74..f00637c 100644 --- a/frontend/src/pages/LandingPage.tsx +++ b/frontend/src/pages/LandingPage.tsx @@ -1,21 +1,68 @@ -import { Avatar, Box, Stack, Typography } from "@mui/material"; +import { Box, 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"; function LandingPage() { + const [isHalloween, setIsHalloween] = useState(false); + + useEffect(() => { + const currentDate = new Date(); + const halloweenDate = new Date("2024-03-04"); + + setIsHalloween( + currentDate.getMonth() === halloweenDate.getMonth() && + currentDate.getDate() === halloweenDate.getDate() + ); + }, []); + return ( <> - + + + + + {isHalloween + ? "Happy Halloween" + : "Keine Geburtstage oder Jubiläen heute."} + + ); } diff --git a/frontend/src/pages/admin/Import.tsx b/frontend/src/pages/admin/Import.tsx index 16d7f09..1854fb7 100644 --- a/frontend/src/pages/admin/Import.tsx +++ b/frontend/src/pages/admin/Import.tsx @@ -1,13 +1,18 @@ import React, { useState, ChangeEvent, FormEvent } from "react"; +import Alert from "@mui/material/Alert"; import { Box, Button, Typography } from "@mui/material"; import { DataGrid, GridColDef } from "@mui/x-data-grid"; import moment from "moment"; function Import() { + let correct = 0; + let incorrect = 0; const [file, setFile] = useState(null); const [csvData, setCsvData] = useState< Array<{ [key: string]: string } | null> >([]); + const [correctCount, setCorrectCount] = useState(0); + const [incorrectCount, setIncorrectCount] = useState(0); const fileReader = new FileReader(); @@ -37,6 +42,25 @@ function Import() { .filter(Boolean); setCsvData(newArray); + + newArray.forEach((row) => { + let isValid = true; + for (const key in row) { + if (!key.includes("name") && row[key]) { + if (!moment(row[key], "DD.MM.YYYY", false).isValid()) { + isValid = false; + break; + } + } + } + if (isValid) { + correct++; + } else { + incorrect++; + } + }); + setCorrectCount(correct); + setIncorrectCount(incorrect); }; const handleOnSubmit = (e: FormEvent) => { @@ -70,8 +94,31 @@ function Import() { {csvData.length > 0 ? (
Fehlerhafte Daten + + + {incorrectCount} von {correctCount + incorrectCount} Datensätze + enthalten fehlerhafte Daten und wurden nicht importiert.Üperprüfen Sie die Daten und korrigieren Sie die Fehler. + ({ id: index, ...row }))} + rows={(csvData || []) + .map((row, index) => ({ id: index, ...row })) + .filter((row: { [key: string]: any }) => { + for (const key in row) { + if (row.hasOwnProperty(key)) { + const value = row[key]; + if ( + !key.includes("name") && + value && + value.length > 0 && + !moment(value, "DD.MM.YYYY", false).isValid() + ) { + return true; + } + } + } + correct++; + return false; + })} columns={ csvData.length > 0 ? Object.keys(csvData[0] || {}).map( @@ -79,7 +126,7 @@ function Import() { ({ field: header, headerName: header.replace(/"/g, ""), - width: 150, + width: 250, editable: true, valueFormatter: (params) => { if (params.value) { @@ -88,15 +135,21 @@ function Import() { params.value, "DD.MM.YYYY", false - ).isValid() && !header.includes("name") + ).isValid() ) { return params.value; } - - return `Falsches Datum: ${params.value}`; + if ( + !header.includes("name") && + params.value.length > 0 + ) { + incorrect++; + return `Error: ${params.value}`; } - return ""; - }, + correct++; + return params.value; + } + }, } as GridColDef) ) : [] @@ -106,6 +159,14 @@ function Import() { ) : (

Keine Daten zum anzeigen

)} + ); } diff --git a/frontend/src/pages/admin/Kalender.tsx b/frontend/src/pages/admin/Kalender.tsx index 4bca327..8d2333d 100644 --- a/frontend/src/pages/admin/Kalender.tsx +++ b/frontend/src/pages/admin/Kalender.tsx @@ -1,11 +1,24 @@ - - +import React from "react"; +import { Typography } from "@mui/material"; +import axios from "axios"; +import Calendar from "react-calendar"; function Kalender() { - return ( -
- -
- ); - } - export default Kalender; \ No newline at end of file + return ( + axios.get('https://feiertage-api.de/api/') + .then(response => { + console.log(response.data); + }) + .catch(error => { + console.log(error); + }), +
+ + Kalender + + + +
+ ); +} +export default Kalender;