diff --git a/backend/rme-time-tracking.sql b/backend/rme-time-tracking.sql new file mode 100644 index 0000000..e2b7427 --- /dev/null +++ b/backend/rme-time-tracking.sql @@ -0,0 +1,53 @@ +-- MariaDB dump 10.19 Distrib 10.4.24-MariaDB, for Win64 (AMD64) +-- +-- Host: localhost Database: rme-time-tracking +-- ------------------------------------------------------ +-- Server version 10.4.24-MariaDB + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `entries` +-- + +DROP TABLE IF EXISTS `entries`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `entries` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `checked_in` time DEFAULT NULL, + `checked_out` time DEFAULT NULL, + `date` date DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `entries` +-- + +LOCK TABLES `entries` WRITE; +/*!40000 ALTER TABLE `entries` DISABLE KEYS */; +INSERT INTO `entries` VALUES (2,'18:15:00','20:12:00','2022-04-30'),(5,'08:00:00','14:00:00','2022-04-28'),(6,'08:00:00','17:00:00','2022-04-22'),(7,'08:04:00','15:23:00','2022-05-30'); +/*!40000 ALTER TABLE `entries` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2022-05-03 21:29:23 diff --git a/backend/routes/api/entry.js b/backend/routes/api/entry.js index 2d683c2..ad3c2de 100644 --- a/backend/routes/api/entry.js +++ b/backend/routes/api/entry.js @@ -106,6 +106,7 @@ router.get("/all/:monthYear?", request_handler.LoggerHandler, (req, res) => { result[i].checked_in, result[i].checked_out ), + result: "-1:20", }); } } diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 9c8cb50..4ccf58e 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,22 +1,17 @@ import "./css/app.scss"; import ServerProvider from "./contexts/ServerContext"; -import Table from "./components/table/Table"; +import TableView from "./views/TableView"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; function App() { return (
-
- - - } /> - - -
- + + + } /> + +
); diff --git a/frontend/src/components/Nav.jsx b/frontend/src/components/Nav.jsx new file mode 100644 index 0000000..5fb0f99 --- /dev/null +++ b/frontend/src/components/Nav.jsx @@ -0,0 +1,157 @@ +import React, { useState, useEffect } from "react"; +import { useParams, useNavigate, useLocation } from "react-router-dom"; + +import "../css/components/nav.scss"; + +import arrows from "../images/arrows.svg"; + +function Nav() { + const params = useParams(); + const navigate = useNavigate(); + const location = useLocation(); + const [monthYear, setMonthYear] = useState(params.monthYear); + const [month, setMonth] = useState(params.month); + const [year, setYear] = useState(params.year); + const [gz, setGz] = useState("+ 01:22"); + const [workingHours, setWorkingHours] = useState("07:00"); + let workingHoursChangeStep = 15; + + const months = [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ]; + + useEffect(() => { + setMonth(monthYear.split("-")[0]); + setYear(monthYear.split("-")[1]); + console.log(gz.substring(0, 1)); + }, [monthYear]); + + useEffect(() => { + setMonthYear(params.monthYear); + }, [location]); + + function nextMonth() { + let month = parseInt(monthYear.split("-")[0]); + let newUrl = ""; + + if (month === 12) { + newUrl = `/table/${1}-${parseInt(monthYear.split("-")[1]) + 1}`; + } else { + newUrl = `/table/${month + 1}-${monthYear.split("-")[1]}`; + } + + navigate(newUrl, { + replace: true, + }); + } + + function previousMonth() { + let month = parseInt(monthYear.split("-")[0]); + let newUrl = ""; + + if (month === 1) { + newUrl = `/table/${12}-${parseInt(monthYear.split("-")[1]) - 1}`; + } else { + newUrl = `/table/${month - 1}-${monthYear.split("-")[1]}`; + } + navigate(newUrl, { + replace: true, + }); + } + + function plusWorkingHours() { + let date = new Date("01/01/2000 " + workingHours); + date.setMinutes(date.getMinutes() + workingHoursChangeStep); + + setWorkingHours( + `${date.getHours() < 10 ? "0" + date.getHours() : date.getHours()}:${ + date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes() + }` + ); + } + + function minusWorkingHours() { + let date = new Date("01/01/2000 " + workingHours); + date.setMinutes(date.getMinutes() - workingHoursChangeStep); + + setWorkingHours( + `${date.getHours() < 10 ? "0" + date.getHours() : date.getHours()}:${ + date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes() + }` + ); + } + + return ( +
+
+
+ + Overview + +
+
+
+
+

+ {gz} GZ +

+
+
+
+
+

+ {months[month - 1]} / {year} +

+
+
+
+
+
+
+ + + +
+
+ + Working hours +
+
+
+

Welcome back, Username!

+ + Log out + +
+
+ ); +} + +export default Nav; diff --git a/frontend/src/components/Sidebar.jsx b/frontend/src/components/Sidebar.jsx new file mode 100644 index 0000000..504dff5 --- /dev/null +++ b/frontend/src/components/Sidebar.jsx @@ -0,0 +1,9 @@ +function Sidebar() { + return ( + + ); +} + +export default Sidebar; diff --git a/frontend/src/components/table/Header.jsx b/frontend/src/components/table/Header.jsx deleted file mode 100644 index 4779b92..0000000 --- a/frontend/src/components/table/Header.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import "../../css/components/table.scss"; - -function Header({ date, checkedIn, checkedOut, ind, norm }) { - return ( -
-
- {date} -
-
- {checkedIn} -
-
- {checkedOut} -
-
- {ind} -
-
- {norm} -
-
- ); -} - -export default Header; diff --git a/frontend/src/components/table/Row.jsx b/frontend/src/components/table/Row.jsx index 4b18ef3..876eaad 100644 --- a/frontend/src/components/table/Row.jsx +++ b/frontend/src/components/table/Row.jsx @@ -1,24 +1,16 @@ -import "../../css/components/table.scss"; +import "../../css/components/table/table.scss"; -function Row({ date, checkedIn, checkedOut, ind, norm }) { +function Row({ weekDay, date, checkedIn, checkedOut, ind, norm, result }) { return ( -
-
- {date ? date : "-"} -
-
- {checkedIn ? checkedIn : "-"} -
-
- {checkedOut ? checkedOut : "-"} -
-
- {ind ? ind : "-"} -
-
- {norm ? norm : "-"} -
-
+ + {weekDay ? weekDay : "-"} + {date ? date : "-"} + {checkedIn ? checkedIn : "-"} + {checkedOut ? checkedOut : "-"} + {ind ? ind : "-"} + {norm ? norm : "-"} + {result ? result : "-"} + ); } diff --git a/frontend/src/components/table/Table.jsx b/frontend/src/components/table/Table.jsx index bb261b2..437350a 100644 --- a/frontend/src/components/table/Table.jsx +++ b/frontend/src/components/table/Table.jsx @@ -1,14 +1,16 @@ +/* eslint-disable react-hooks/exhaustive-deps */ import React, { useState, useContext, useEffect } from "react"; -import { ServerContext } from "../../contexts/ServerContext"; -import { useParams } from "react-router-dom"; -import "../../css/components/table.scss"; +import { ServerContext } from "../../contexts/ServerContext"; +import { useParams, useLocation } from "react-router-dom"; + +import "../../css/components/table/table.scss"; import Row from "./Row"; -import Header from "./Header"; function Table() { const params = useParams(); + const location = useLocation(); const { URL } = useContext(ServerContext); const weekDays = ["Sat.", "Sun.", "Mon.", "Tue.", "Wed.", "Thu.", "Fri."]; const [entries, setEntries] = useState([]); @@ -16,9 +18,11 @@ function Table() { const [rows, setRows] = useState([]); async function fetchEntries() { - let response = await fetch(URL + `/api/entry/all/` + monthYear); - let data = await response.json(); - setEntries(data); + // Set entries after fetching them from the server + let response = await fetch(URL + `/api/entry/all/` + monthYear).then( + (res) => res.json() + ); + setEntries(response); } function daysInMonth() { @@ -28,14 +32,19 @@ function Table() { return new Date(year, month, 0).getDate(); } + useEffect(() => { + setMonthYear(params.monthYear); + }, [location]); + useEffect(() => { fetchEntries(); - }, []); + }, [monthYear]); useEffect(() => { let month = monthYear.split("-")[0]; let year = monthYear.split("-")[1]; - setRows([]); + let temp_rows = []; + for (let i = 1; i <= daysInMonth(); i++) { let date = `${i < 10 ? "0" + i : i}.${ month < 10 ? "0" + month : month @@ -46,65 +55,39 @@ function Table() { let entry = entries.find((entry) => entry.date === date); if (entry) { - setRows((...rows) => [ - rows, + console.log(entry); + temp_rows.push( , - ]); + result={entry.result} + /> + ); } else { - setRows((...rows) => [rows, ]); + temp_rows.push(); } } - console.log(rows); + setRows(temp_rows); }, [entries]); - function nextMonth() { - let month = parseInt(monthYear.split("-")[0]); - - if (month === 12) { - window.location.href = `/table/${1}-${ - parseInt(monthYear.split("-")[1]) + 1 - }`; - } else { - window.location.href = `/table/${month + 1}-${monthYear.split("-")[1]}`; - } - } - - function previousMonth() { - let month = parseInt(monthYear.split("-")[0]); - - if (month === 1) { - window.location.href = `/table/${12}-${ - parseInt(monthYear.split("-")[1]) - 1 - }`; - } else { - window.location.href = `/table/${month - 1}-${monthYear.split("-")[1]}`; - } - } - return ( -
-
-
-

{monthYear}

-
-
-
-
- {/* ROWS */} +
+ + + + + + + + + + {rows} - +
WeekdayDateChecked inChecked outIndNormResult
); } diff --git a/frontend/src/css/App.scss b/frontend/src/css/App.scss index 3f62f07..109d731 100644 --- a/frontend/src/css/App.scss +++ b/frontend/src/css/App.scss @@ -1,37 +1,3 @@ #app { - display: grid; - grid-template-columns: 2fr 1fr; height: 100vh; - - .app-container { - display: flex; - height: 100%; - grid-area: auto; - padding-left: 20px; - padding-right: 20px; - } - - #content { - } - - #sidebar { - border-left: 4px solid #575757; - } -} - -@media screen and (max-width: 1000px) { - #app { - grid-template-columns: 1fr 1fr; - } -} - -@media screen and (max-width: 730px) { - #app { - grid-template-columns: 1fr; - } - - #sidebar { - border-left: 0px solid transparent !important; - border-top: 4px solid #575757; - } } diff --git a/frontend/src/css/components/nav.scss b/frontend/src/css/components/nav.scss new file mode 100644 index 0000000..08064f0 --- /dev/null +++ b/frontend/src/css/components/nav.scss @@ -0,0 +1,195 @@ +.nav { + width: 100%; + height: 30px; + background-color: rgb(13, 13, 13); + display: grid; + grid-template-columns: 0.5fr 0.5fr 1fr 0.5fr 0.5fr; + padding-top: 10px; + padding-bottom: 10px; + + a { + border: 1px solid transparent; + font-weight: bold; + color: white; + text-decoration: none; + padding: 5px; + padding-top: 3px; + border-radius: 2px; + transition: all 100ms linear; + + &:hover { + box-shadow: 0px 0px 25px -10px #9f9f9f; + } + } + + div { + grid-area: auto; + + .nav-overview { + margin-top: 3px; + margin-left: 30px; + a { + background-color: #0f9f5f; + + &:hover { + background-color: #10b374; + border: 1px solid #61ffc2; + } + } + } + + .nav-info { + p { + font-weight: bold; + font-size: 15pt; + } + + .text-positive { + color: #0f9f5f; + } + + .text-negative { + color: #ff0000; + } + } + + .working-hours { + height: 100%; + justify-items: center; + + small { + display: block; + height: 10px; + width: 150px; + margin-top: 0px; + line-height: 12px; + font-size: 9pt; + margin-left: auto; + margin-right: auto; + text-align: center; + font-weight: bold; + color: #0f9f5f; + } + + .working-hours-input-block { + display: block; + width: 150px; + margin-left: auto; + margin-right: auto; + + .working-hours-input-container { + display: flex; + height: 100%; + justify-items: center; + button { + background-color: #0f9f5f; + border: 2px solid transparent; + font-weight: bold; + font-size: 25pt; + color: white; + text-decoration: none; + width: 25px; + height: 25px; + transition: all 100ms linear; + cursor: pointer; + text-align: center; + + &:hover { + background-color: #10b374; + border: 2px solid #61ffc2; + box-shadow: 0px 0px 25px -10px #9f9f9f; + } + } + + #workingHoursBtnPlus { + line-height: 18px; + } + + #workingHoursBtnMinus p { + // Fucky weil ein minus nicht genau in er mitte ist vertikal.......... :/ + font-size: 30pt; + line-height: 10px; + padding-bottom: 5px; + font-family: Verdana, Geneva, Tahoma, sans-serif; + vertical-align: middle; + } + + input { + width: calc(100% - 25px * 2); + height: 23px; + border: 1px solid #0f9f5f; + text-align: center; + font-size: 15pt; + background-color: #b1dac8; + outline: 0; + transition: all 100ms linear; + -moz-appearance: textfield; + + &:hover { + border: 1px solid #10b374; + box-shadow: 0px 0px 25px -10px #9f9f9f; + } + + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } + } + } + } + } + } + + .month-navigation { + display: grid; + grid-template-columns: 1fr 2fr 1fr; + + div { + background-color: transparent; + width: 30px; + height: 30px; + display: block; + margin-left: auto; + margin-right: auto; + cursor: pointer; + transition: all 50ms linear; + background-repeat: no-repeat; + background-position: 0 0; + filter: brightness(0.6); + + &:hover { + filter: brightness(1); + } + } + + .nav-arrows-previous { + transform: rotate(180deg); + } + + h3 { + text-align: center; + padding-bottom: 10px; + } + } + .nav-account { + display: flex; + + p { + padding-top: 3px; + font-size: 12pt; + font-weight: bold; + color: #0f9f5f; + } + .logout-a { + margin-left: 20px; + background-color: #9f0f0f; + margin-bottom: 3px; + + &:hover { + background-color: #e62f2f; + border: 1px solid #f07a7a; + } + } + } +} diff --git a/frontend/src/css/components/table.scss b/frontend/src/css/components/table.scss deleted file mode 100644 index 08a9522..0000000 --- a/frontend/src/css/components/table.scss +++ /dev/null @@ -1,59 +0,0 @@ -#table { - height: 100%; - width: 100%; - - .table-nav { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - padding-top: 10px; - padding-bottom: 10px; - - div { - background-color: red; - width: 50px; - height: 50px; - display: block; - margin-left: auto; - margin-right: auto; - cursor: pointer; - } - - h2 { - text-align: center; - padding-top: 10px; - } - } - - .table-container { - height: 100%; - width: 100%; - - text-align: center; - font-size: 1rem; - - .table-header { - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr 1fr; - width: 100%; - background-color: rgb(28, 28, 28); - font-size: 1.2rem; - font-weight: bold; - border-bottom: 2px solid rgb(157, 157, 157); - } - - .table-row { - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr 1fr; - width: 100%; - background-color: rgb(61, 61, 61); - border-top: 1px solid rgb(85, 85, 85); - } - - .table-cell { - grid-area: auto; - padding: 10px; - border-right: 1px solid rgb(85, 85, 85); - border-left: 1px solid rgb(85, 85, 85); - } - } -} diff --git a/frontend/src/css/components/table/table.scss b/frontend/src/css/components/table/table.scss new file mode 100644 index 0000000..6bb3e6f --- /dev/null +++ b/frontend/src/css/components/table/table.scss @@ -0,0 +1,35 @@ +.table-container { + height: 100%; + width: 100%; + white-space: nowrap; + text-overflow: ellipsis; + overflow-x: hidden; + + table { + width: 100%; + height: 100%; + margin-bottom: 50px; + border-collapse: collapse; + overflow-x: hidden; + + tr { + height: 40px; + text-align: center; + background-color: #323232; + &:nth-child(even) { + background-color: #1d1d1d; + } + + th { + padding-top: 2px; + padding-bottom: 2px; + background-color: #131313; + } + + td { + padding-top: 4px; + padding-bottom: 4px; + } + } + } +} diff --git a/frontend/src/css/index.scss b/frontend/src/css/index.scss index f5a8050..ee7518a 100644 --- a/frontend/src/css/index.scss +++ b/frontend/src/css/index.scss @@ -1,3 +1,8 @@ +// Scrollbar styling +$scrollbar-background-color: rgba(255, 255, 255, 0); +$scrollbar-foreground-color: rgb(216, 242, 242); +$scrollbar-width: 6px; + * { margin: 0; padding: 0; @@ -14,4 +19,20 @@ html { height: 100vh; color: white; background-color: #2b2b2b; + overflow: hidden; +} + +/* Scrollbar Webkit */ +*::-webkit-scrollbar { + width: $scrollbar-width; +} + +*::-webkit-scrollbar-track { + background: $scrollbar-background-color; +} + +*::-webkit-scrollbar-thumb { + background-color: $scrollbar-foreground-color; + border-radius: 10px; + border: 3px solid transparent; } diff --git a/frontend/src/css/views/tableView.scss b/frontend/src/css/views/tableView.scss new file mode 100644 index 0000000..8afe458 --- /dev/null +++ b/frontend/src/css/views/tableView.scss @@ -0,0 +1,12 @@ +#tableView { + height: 100%; + .tableView-container { + display: grid; + grid-template-columns: 1fr 0.5fr; + height: 100%; + + div { + grid-area: auto; + } + } +} diff --git a/frontend/src/images/arrows.svg b/frontend/src/images/arrows.svg new file mode 100644 index 0000000..2668070 --- /dev/null +++ b/frontend/src/images/arrows.svg @@ -0,0 +1 @@ + Asset 15 \ No newline at end of file diff --git a/frontend/src/views/TableView.jsx b/frontend/src/views/TableView.jsx new file mode 100644 index 0000000..b38894f --- /dev/null +++ b/frontend/src/views/TableView.jsx @@ -0,0 +1,18 @@ +import Nav from "../components/Nav.jsx"; +import Sidebar from "../components/Sidebar.jsx"; +import Table from "../components/table/Table.jsx"; +import "../css/views/tableView.scss"; + +function TableView() { + return ( +
+