Compare commits
149
Commits
0.1.0
...
7b5ce23342
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b5ce23342 | ||
|
|
a3b123f466 | ||
|
|
d52a9d77ab | ||
|
|
a09edcae25 | ||
|
|
91849375c7 | ||
|
|
63ded6da57 | ||
|
|
9a65c7f78b | ||
|
|
b89a3c744f | ||
|
|
85d22d5c06 | ||
|
|
6fa6b28af6 | ||
|
|
41a3c79c91 | ||
|
|
82aad9766e | ||
|
|
b094cf8180 | ||
|
|
dff3c7f34d | ||
|
|
aa66d1f53d | ||
|
|
af61eb2334 | ||
|
|
616ed6383e | ||
|
|
e187bdeab7 | ||
|
|
c35b3a86f3 | ||
|
|
4b12eb3897 | ||
|
|
3452aa9999 | ||
|
|
072e99c6fb | ||
|
|
608d96ac4e | ||
|
|
51b5eb423c | ||
|
|
6e9ec73db4 | ||
|
|
1e48a5b608 | ||
|
|
dc7d84e35f | ||
|
|
5bbcc961e6 | ||
|
|
94b22c1d26 | ||
|
|
99cee223de | ||
|
|
fbbb563aad | ||
|
|
d7e99cbf30 | ||
|
|
3e291171a8 | ||
|
|
7ef5c43901 | ||
|
|
9932f33c34 | ||
|
|
06128f72fc | ||
|
|
18c5c45c26 | ||
|
|
0a21c6ed35 | ||
|
|
47ed408185 | ||
|
|
0813fd637b | ||
|
|
094b50d33d | ||
|
|
3bd3e6837a | ||
|
|
4da284a0a6 | ||
|
|
084abbf42c | ||
|
|
bcc6448fa1 | ||
|
|
1fc54f4b89 | ||
|
|
d4ddd4f32c | ||
|
|
90c6ef0c53 | ||
|
|
1ebf57ac3b | ||
|
|
13554d09e0 | ||
|
|
b46f5634c8 | ||
|
|
77cfcc72b4 | ||
|
|
ea93400135 | ||
|
|
646537e19c | ||
|
|
efadeb501b | ||
|
|
cf50607f43 | ||
|
|
d133cb017b | ||
|
|
ad253a82f5 | ||
|
|
fb91d1121e | ||
|
|
e10b72e086 | ||
|
|
aae4764f5e | ||
|
|
371425f3c5 | ||
|
|
c3a1953cc9 | ||
|
|
66022e42df | ||
|
|
907ce88ed1 | ||
|
|
45650a0f9a | ||
|
|
d13fce569b | ||
|
|
fafc2a0ad5 | ||
|
|
f3ff303b93 | ||
|
|
3dd0f451fc | ||
|
|
46313cc290 | ||
|
|
65195e702e | ||
|
|
ec22f42623 | ||
|
|
b43b6382d7 | ||
|
|
12ed91a6da | ||
|
|
cc9941d1d3 | ||
|
|
2b2753e009 | ||
|
|
735b195c06 | ||
|
|
0bd8932fa1 | ||
|
|
114e31029c | ||
|
|
de0535be32 | ||
|
|
b74e9bbd69 | ||
|
|
338c3ce5e4 | ||
|
|
1b4983a06f | ||
|
|
74193f3b46 | ||
|
|
2e0e6db55a | ||
|
|
c528944746 | ||
|
|
05d46ec407 | ||
|
|
7b48e7e9e2 | ||
|
|
343f12b605 | ||
|
|
6203f98e93 | ||
|
|
87f3bb3f0e | ||
|
|
8d00e0c104 | ||
|
|
3fe73f3b10 | ||
|
|
b5af0c5a54 | ||
|
|
e29a6dcab9 | ||
|
|
8ee9666250 | ||
|
|
edcb80421b | ||
|
|
16e05ce819 | ||
|
|
5c15580738 | ||
|
|
0a5e40d7cb | ||
|
|
ae7491f002 | ||
|
|
8296a2a281 | ||
|
|
c711e0ae24 | ||
|
|
74fd96cf25 | ||
|
|
9686a8505e | ||
|
|
bc83fc8dd1 | ||
|
|
9d5af9a0fb | ||
|
|
7f8a50799f | ||
|
|
365c2f041c | ||
|
|
89ad394773 | ||
|
|
881e7362f8 | ||
|
|
c8d9bc040e | ||
|
|
e3ef53b119 | ||
|
|
f78fdfbbd5 | ||
|
|
02771828c9 | ||
|
|
5a88ff3c6f | ||
|
|
f36324bfbd | ||
|
|
9b99c6a4dc | ||
|
|
fe3a4f69bf | ||
|
|
31a3f4cc01 | ||
|
|
0a59cb60db | ||
|
|
9430f3c74c | ||
|
|
2b31e17e14 | ||
|
|
9e35ab3b07 | ||
|
|
056bf82c7b | ||
|
|
be54a659c9 | ||
|
|
8086b86eb2 | ||
|
|
3e2865dbe7 | ||
|
|
60eb1f92e8 | ||
|
|
9a542c9108 | ||
|
|
a035ea9648 | ||
|
|
46f3a6491d | ||
|
|
e070db18b5 | ||
|
|
a6675605e4 | ||
|
|
520db45d6d | ||
|
|
2d3a1074f6 | ||
|
|
a1e8b1aa51 | ||
|
|
f25c9d8994 | ||
|
|
3854737622 | ||
|
|
d16a4e427d | ||
|
|
4871ff14dc | ||
|
|
ac99e079ee | ||
|
|
09ffadc596 | ||
|
|
4da5c1242c | ||
|
|
e0b8deae8b | ||
|
|
d3df8bdc7e | ||
|
|
b53b1cdd3c | ||
|
|
0054ab5afc |
@@ -0,0 +1,10 @@
|
|||||||
|
# http://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = false
|
||||||
+8
-1
@@ -2,10 +2,13 @@
|
|||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
/.idea/*
|
||||||
|
|
||||||
# User-specific stuff
|
# User-specific stuff
|
||||||
.idea/**/workspace.xml
|
.idea/**/workspace.xml
|
||||||
.idea/**/tasks.xml
|
.idea/**/tasks.xml
|
||||||
.idea/**/usage.statistics.xml
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/markdown.xml
|
||||||
.idea/**/dictionaries
|
.idea/**/dictionaries
|
||||||
.idea/**/shelf
|
.idea/**/shelf
|
||||||
|
|
||||||
@@ -105,4 +108,8 @@ Temporary Items
|
|||||||
/api/vendor/
|
/api/vendor/
|
||||||
/api/tests/_*
|
/api/tests/_*
|
||||||
/api/tests/*.suite.yml
|
/api/tests/*.suite.yml
|
||||||
/api/html/swagger/
|
/api/.env
|
||||||
|
/ui/static/print.css*
|
||||||
|
/ui/static/global.css*
|
||||||
|
/ui/static/bundle.css*
|
||||||
|
/ui/.svelte-kit/
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
## Datenbank-Einstellungen
|
||||||
|
DATABASES_HOST=psql.domain.tld
|
||||||
|
DATABASES_NAME=db
|
||||||
|
DATABASES_USER=user
|
||||||
|
DATABASES_PASS=1234
|
||||||
|
|
||||||
|
## CORS Einstellungen
|
||||||
|
CORS_ALLOW_ORIGIN_REGEX=/https?:\/\/localhost:\d+/
|
||||||
+4
-1
@@ -1,4 +1,7 @@
|
|||||||
FROM php:8.0-apache
|
FROM php:8.4-apache-bookworm
|
||||||
|
|
||||||
|
RUN apt update && apt install -y libpq-dev
|
||||||
|
RUN docker-php-ext-install -j$(nproc) pdo pdo_pgsql
|
||||||
|
|
||||||
WORKDIR /var/www
|
WORKDIR /var/www
|
||||||
|
|
||||||
|
|||||||
+15
-9
@@ -16,18 +16,22 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
|
"php": "8.4.*",
|
||||||
"slim/slim": "^4.7.1",
|
"slim/slim": "^4.7.1",
|
||||||
"vlucas/phpdotenv": "^4.2",
|
"vlucas/phpdotenv": "^v5.6.1",
|
||||||
"slim/psr7": "^1.3",
|
"slim/psr7": "^1.7.0",
|
||||||
|
"php-di/slim-bridge": "^3.1.0",
|
||||||
"jetbrains/phpstorm-attributes": "^1.0.0",
|
"jetbrains/phpstorm-attributes": "^1.0.0",
|
||||||
"myclabs/php-enum": "^1.8.0"
|
"myclabs/php-enum": "^1.8.0",
|
||||||
|
"ext-pdo": "*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^0.12.80",
|
"phpstan/phpstan": "^2.1.12",
|
||||||
"codeception/codeception": "^4.1.18",
|
"codeception/codeception": "^5.2.1",
|
||||||
"codeception/module-phpbrowser": "^1.0.0",
|
"codeception/module-phpbrowser": "^3.0.1",
|
||||||
"codeception/module-asserts": "^1.0.0",
|
"codeception/module-asserts": "^3.1.0",
|
||||||
"codeception/module-db": "^1.1.0"
|
"codeception/module-db": "^3.2.2",
|
||||||
|
"codeception/module-rest": "^3.4.1"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
@@ -36,7 +40,9 @@
|
|||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"TorstenHettstedt\\TimekeepingApi\\Tests\\Unit\\": "tests/unit"
|
"TorstenHettstedt\\TimekeepingApi\\Tests\\Unit\\": "tests/unit",
|
||||||
|
"TorstenHettstedt\\TimekeepingApi\\Tests\\Api\\": "tests/api",
|
||||||
|
"Helper\\": "tests/_support/Helper"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
# absolute physical path to the directory that contains this htaccess file.
|
# absolute physical path to the directory that contains this htaccess file.
|
||||||
# RewriteBase /
|
# RewriteBase /
|
||||||
|
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
RewriteRule ^ index.php [QSA,L]
|
RewriteRule ^ index.php [QSA,L]
|
||||||
</IfModule>
|
</IfModule>
|
||||||
+44
-2
@@ -1,12 +1,54 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../vendor/autoload.php';
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__));
|
||||||
|
$dotenv->load();
|
||||||
|
|
||||||
|
use DI\Container;
|
||||||
use Slim\Factory\AppFactory;
|
use Slim\Factory\AppFactory;
|
||||||
|
use Slim\Routing\RouteCollectorProxy;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Controller\PreflightController;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Controller\WorkingHoursController;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Controller\WorkingHoursViewController;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Middleware\ErrorHandler;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Middleware\JsonBodyParserMiddleware;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Middleware\OriginAccessControlHandler;
|
||||||
|
|
||||||
|
$container = new Container();
|
||||||
|
|
||||||
|
$container->set('databases', function () {
|
||||||
|
$dsn = "pgsql:host={$_ENV['DATABASES_HOST']};port=5432;dbname={$_ENV['DATABASES_NAME']}";
|
||||||
|
return new PDO($dsn, $_ENV['DATABASES_USER'], $_ENV['DATABASES_PASS']);
|
||||||
|
});
|
||||||
|
|
||||||
|
AppFactory::setContainer($container);
|
||||||
$app = AppFactory::create();
|
$app = AppFactory::create();
|
||||||
|
$app->add(new JsonBodyParserMiddleware());
|
||||||
|
$app->addBodyParsingMiddleware();
|
||||||
|
$app->add(new OriginAccessControlHandler());
|
||||||
|
$app->addRoutingMiddleware();
|
||||||
|
|
||||||
$app->get('/', 'TorstenHettstedt\TimekeepingApi\Controller\HelloWorldController:read');
|
$app->group('/views/working-hours', function (RouteCollectorProxy $group) {
|
||||||
|
$group->options('/weekly', PreflightController::class . ':preflight');
|
||||||
|
$group->get('/weekly', WorkingHoursViewController::class . ':browseWeekly');
|
||||||
|
$group->options('/monthly', PreflightController::class . ':preflight');
|
||||||
|
$group->get('/monthly', WorkingHoursViewController::class . ':browseMonthly');
|
||||||
|
$group->options('/yearly', PreflightController::class . ':preflight');
|
||||||
|
$group->get('/yearly', WorkingHoursViewController::class . ':browseYearly');
|
||||||
|
});
|
||||||
|
|
||||||
$app->addErrorMiddleware(true, true, true);
|
$app->group('/working-hours', function (RouteCollectorProxy $group) {
|
||||||
|
$group->options('', PreflightController::class . ':preflight');
|
||||||
|
$group->get('', WorkingHoursController::class . ':browse');
|
||||||
|
$group->post('', WorkingHoursController::class . ':creat');
|
||||||
|
$group->group('/{id:\d\d\d\d-\d\d-\d\d}', function (RouteCollectorProxy $group) {
|
||||||
|
$group->options('', PreflightController::class . ':preflight');
|
||||||
|
$group->get('', WorkingHoursController::class . ':read');
|
||||||
|
$group->put('', WorkingHoursController::class . ':update');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
|
||||||
|
$errorMiddleware->setDefaultErrorHandler(new ErrorHandler($app));
|
||||||
|
|
||||||
$app->run();
|
$app->run();
|
||||||
+37
-4
@@ -18,6 +18,15 @@
|
|||||||
"tags": [
|
"tags": [
|
||||||
"Eintragungen Arbeitszeiten"
|
"Eintragungen Arbeitszeiten"
|
||||||
],
|
],
|
||||||
|
"description": "Liefert eine Liste der Einträge. Kann gefiltert werden und dadurch eine leere Liste zurückgeben",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/start-date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/end-date"
|
||||||
|
}
|
||||||
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"$ref": "#/components/responses/ListEntities"
|
"$ref": "#/components/responses/ListEntities"
|
||||||
@@ -82,6 +91,7 @@
|
|||||||
"tags": [
|
"tags": [
|
||||||
"Eintragungen Arbeitszeiten"
|
"Eintragungen Arbeitszeiten"
|
||||||
],
|
],
|
||||||
|
"description": "Angabe des Datum im Request-Body werden ignoriert",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"$ref": "#/components/parameters/date"
|
"$ref": "#/components/parameters/date"
|
||||||
@@ -167,14 +177,17 @@
|
|||||||
"description": "Eintrag der Arbeitszeit für einen Tag",
|
"description": "Eintrag der Arbeitszeit für einen Tag",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"date": {
|
"workingDay": {
|
||||||
"description": "Datum eines Arbeitstages",
|
"description": "Datum eines Arbeitstages",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "date"
|
"format": "date",
|
||||||
|
"required": false
|
||||||
},
|
},
|
||||||
"workingHours": {
|
"workingTime": {
|
||||||
"description": "Arbeitszeit des Tages",
|
"description": "Arbeitszeit des Tages",
|
||||||
"type": "integer"
|
"type": "string",
|
||||||
|
"pattern": "[0-5]\\d:[0-5]\\d:[0-5]\\d",
|
||||||
|
"example": "08:01:00"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -255,6 +268,26 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "date"
|
"format": "date"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"start-date" : {
|
||||||
|
"description": "Filtert Liste ab Datum",
|
||||||
|
"name": "start-date",
|
||||||
|
"in" : "query",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"end-date" : {
|
||||||
|
"description": "Filtert Liste bis Datum",
|
||||||
|
"name": "end-date",
|
||||||
|
"in" : "query",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"responses": {
|
"responses": {
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 665 B |
Binary file not shown.
|
After Width: | Height: | Size: 628 B |
@@ -0,0 +1,60 @@
|
|||||||
|
<!-- HTML for static distribution bundle build -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Swagger UI</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="swagger-ui.css" >
|
||||||
|
<link rel="icon" type="image/png" href="favicon-32x32.png" sizes="32x32" />
|
||||||
|
<link rel="icon" type="image/png" href="favicon-16x16.png" sizes="16x16" />
|
||||||
|
<style>
|
||||||
|
html
|
||||||
|
{
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: -moz-scrollbars-vertical;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after
|
||||||
|
{
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
body
|
||||||
|
{
|
||||||
|
margin:0;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="swagger-ui"></div>
|
||||||
|
|
||||||
|
<script src="swagger-ui-bundle.js"> </script>
|
||||||
|
<script src="swagger-ui-standalone-preset.js"> </script>
|
||||||
|
<script>
|
||||||
|
window.onload = function() {
|
||||||
|
// Begin Swagger UI call region
|
||||||
|
const ui = SwaggerUIBundle({
|
||||||
|
url: "../openapi.json",
|
||||||
|
dom_id: '#swagger-ui',
|
||||||
|
deepLinking: true,
|
||||||
|
presets: [
|
||||||
|
SwaggerUIBundle.presets.apis,
|
||||||
|
SwaggerUIStandalonePreset
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
SwaggerUIBundle.plugins.DownloadUrl
|
||||||
|
],
|
||||||
|
layout: "StandaloneLayout"
|
||||||
|
})
|
||||||
|
// End Swagger UI call region
|
||||||
|
|
||||||
|
window.ui = ui
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en-US">
|
||||||
|
<title>Swagger UI: OAuth2 Redirect</title>
|
||||||
|
<body onload="run()">
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
function run () {
|
||||||
|
var oauth2 = window.opener.swaggerUIRedirectOauth2;
|
||||||
|
var sentState = oauth2.state;
|
||||||
|
var redirectUrl = oauth2.redirectUrl;
|
||||||
|
var isValid, qp, arr;
|
||||||
|
|
||||||
|
if (/code|token|error/.test(window.location.hash)) {
|
||||||
|
qp = window.location.hash.substring(1);
|
||||||
|
} else {
|
||||||
|
qp = location.search.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
arr = qp.split("&")
|
||||||
|
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
|
||||||
|
qp = qp ? JSON.parse('{' + arr.join() + '}',
|
||||||
|
function (key, value) {
|
||||||
|
return key === "" ? value : decodeURIComponent(value)
|
||||||
|
}
|
||||||
|
) : {}
|
||||||
|
|
||||||
|
isValid = qp.state === sentState
|
||||||
|
|
||||||
|
if ((
|
||||||
|
oauth2.auth.schema.get("flow") === "accessCode"||
|
||||||
|
oauth2.auth.schema.get("flow") === "authorizationCode"
|
||||||
|
) && !oauth2.auth.code) {
|
||||||
|
if (!isValid) {
|
||||||
|
oauth2.errCb({
|
||||||
|
authId: oauth2.auth.name,
|
||||||
|
source: "auth",
|
||||||
|
level: "warning",
|
||||||
|
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qp.code) {
|
||||||
|
delete oauth2.state;
|
||||||
|
oauth2.auth.code = qp.code;
|
||||||
|
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
|
||||||
|
} else {
|
||||||
|
let oauthErrorMsg
|
||||||
|
if (qp.error) {
|
||||||
|
oauthErrorMsg = "["+qp.error+"]: " +
|
||||||
|
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
|
||||||
|
(qp.error_uri ? "More info: "+qp.error_uri : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
oauth2.errCb({
|
||||||
|
authId: oauth2.auth.name,
|
||||||
|
source: "auth",
|
||||||
|
level: "error",
|
||||||
|
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
|
||||||
|
}
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Controller;
|
||||||
|
|
||||||
|
use JsonSerializable;
|
||||||
|
use PDO;
|
||||||
|
use Psr\Container\ContainerExceptionInterface;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
|
use Slim\Psr7\Response;
|
||||||
|
|
||||||
|
abstract class AbstractController
|
||||||
|
{
|
||||||
|
|
||||||
|
protected PDO $databases;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WorkingHoursController constructor.
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $container
|
||||||
|
*
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
*/
|
||||||
|
public function __construct(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
if ($container->has('databases') === false) {
|
||||||
|
throw new NotDatabasesException('Datenbank ist nicht Vorhanden');
|
||||||
|
}
|
||||||
|
$this->databases = $container->get('databases');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Response $response
|
||||||
|
* @param JsonSerializable|JsonSerializable[] $data
|
||||||
|
* @param int $status_code
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
protected function printResponse(Response $response, mixed $data, int $status_code): Response
|
||||||
|
{
|
||||||
|
$payload = json_encode($data);
|
||||||
|
|
||||||
|
$response->getBody()->write($payload);
|
||||||
|
|
||||||
|
return $response
|
||||||
|
->withHeader('Content-Type', 'application/json')
|
||||||
|
->withStatus($status_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<?php /** @noinspection PhpUnused */
|
|
||||||
|
|
||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Controller;
|
|
||||||
|
|
||||||
use Slim\Psr7\Response;
|
|
||||||
use Slim\Psr7\Request;
|
|
||||||
|
|
||||||
class HelloWorldController
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param Request $request
|
|
||||||
* @param Response $response
|
|
||||||
* @param mixed[] $args
|
|
||||||
*
|
|
||||||
* @return Response
|
|
||||||
*
|
|
||||||
* @noinspection PhpUnusedParameterInspection
|
|
||||||
*/
|
|
||||||
public function read(Request $request, Response $response, array $args): Response
|
|
||||||
{
|
|
||||||
$payload = json_encode('Hallo World');
|
|
||||||
|
|
||||||
$response->getBody()->write($payload);
|
|
||||||
|
|
||||||
return $response
|
|
||||||
->withHeader('Content-Type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php /** @noinspection PhpMissingFieldTypeInspection */
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Controller;
|
||||||
|
|
||||||
|
use Slim\Exception\HttpSpecializedException;
|
||||||
|
|
||||||
|
class HttpConflictRequestException extends HttpSpecializedException
|
||||||
|
{
|
||||||
|
/** @var int */
|
||||||
|
protected $code = 409;
|
||||||
|
/** @var string */
|
||||||
|
protected $message = 'Conflict.';
|
||||||
|
protected string $title = '409 Conflict';
|
||||||
|
protected string $description = 'The 409 (Conflict) status code indicates that the request could not be completed due to a conflict with the current state of the target resource.';
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Controller;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class NotDatabasesException extends Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Controller;
|
||||||
|
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Slim\Psr7\Request;
|
||||||
|
use Slim\Psr7\Response;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Middleware\OriginAccessControlHandler;
|
||||||
|
|
||||||
|
class PreflightController
|
||||||
|
{
|
||||||
|
/** @noinspection PhpUnused */
|
||||||
|
public function preflight(Request $request, Response $response): ResponseInterface
|
||||||
|
{
|
||||||
|
return (new OriginAccessControlHandler())->originAccessControl($request, $response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Controller;
|
||||||
|
|
||||||
|
use DateInterval;
|
||||||
|
use DateTime;
|
||||||
|
use Exception;
|
||||||
|
use Fig\Http\Message\StatusCodeInterface;
|
||||||
|
use Slim\Exception\HttpBadRequestException;
|
||||||
|
use Slim\Exception\HttpInternalServerErrorException;
|
||||||
|
use Slim\Exception\HttpNotFoundException;
|
||||||
|
use Slim\Psr7\Request;
|
||||||
|
use Slim\Psr7\Response;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Models\WorkingHours;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryBadWhereDataException;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordAlreadyExistException;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordNotFoundException;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursRepository;
|
||||||
|
|
||||||
|
class WorkingHoursController extends AbstractController
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array<string, mixed> $args
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*
|
||||||
|
* @throws HttpBadRequestException
|
||||||
|
* @throws HttpNotFoundException
|
||||||
|
*/
|
||||||
|
public function update(Request $request, Response $response, array $args): Response
|
||||||
|
{
|
||||||
|
$body = $request->getParsedBody();
|
||||||
|
$repository = new WorkingHoursRepository($this->databases);
|
||||||
|
$model = $this->buildModel($request, $args['id'], $body['workingTime']);
|
||||||
|
try {
|
||||||
|
$repository->update($model);
|
||||||
|
} catch (RepositoryRecordNotFoundException $exception) {
|
||||||
|
throw new HttpNotFoundException($request, 'Der Eintrag ist nicht vorhanden.', $exception);
|
||||||
|
}
|
||||||
|
return $this->printResponse($response, $model, StatusCodeInterface::STATUS_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array<string, mixed> $args
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*
|
||||||
|
* @throws HttpInternalServerErrorException
|
||||||
|
* @throws HttpBadRequestException
|
||||||
|
* @noinspection PhpUnusedParameterInspection
|
||||||
|
*/
|
||||||
|
public function browse(Request $request, Response $response, array $args): Response
|
||||||
|
{
|
||||||
|
$repository = new WorkingHoursRepository($this->databases);
|
||||||
|
$queryParams = $request->getQueryParams();
|
||||||
|
try {
|
||||||
|
return $this->printResponse(
|
||||||
|
$response,
|
||||||
|
$repository->findFiltered(
|
||||||
|
$queryParams['start-date'] ?? null,
|
||||||
|
$queryParams['end-date'] ?? null
|
||||||
|
),
|
||||||
|
StatusCodeInterface::STATUS_OK
|
||||||
|
);
|
||||||
|
} catch (RepositoryBadWhereDataException $exception) {
|
||||||
|
throw new HttpBadRequestException($request, 'Ein Wert für das Datum im Query ist ungültig', $exception);
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
throw new HttpInternalServerErrorException(
|
||||||
|
$request,
|
||||||
|
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank',
|
||||||
|
$exception
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array<string, mixed> $args
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*
|
||||||
|
* @throws HttpNotFoundException
|
||||||
|
* @throws HttpInternalServerErrorException
|
||||||
|
*/
|
||||||
|
public function read(Request $request, Response $response, array $args): Response
|
||||||
|
{
|
||||||
|
$repository = new WorkingHoursRepository($this->databases);
|
||||||
|
try {
|
||||||
|
return $this->printResponse($response, $repository->findByKey($args['id']), StatusCodeInterface::STATUS_OK);
|
||||||
|
} catch (RepositoryRecordNotFoundException $exception) {
|
||||||
|
throw new HttpNotFoundException($request, 'Der Eintrag ist nicht vorhanden.', $exception);
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
throw new HttpInternalServerErrorException(
|
||||||
|
$request,
|
||||||
|
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank',
|
||||||
|
$exception
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array<string, mixed> $args
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*
|
||||||
|
* @throws HttpBadRequestException
|
||||||
|
* @throws HttpConflictRequestException
|
||||||
|
* @throws HttpInternalServerErrorException
|
||||||
|
* @noinspection PhpUnusedParameterInspection
|
||||||
|
*/
|
||||||
|
public function creat(Request $request, Response $response, array $args): Response
|
||||||
|
{
|
||||||
|
$body = $request->getParsedBody();
|
||||||
|
$repository = new WorkingHoursRepository($this->databases);
|
||||||
|
$model = $this->buildModel($request, $body['workingDay'], $body['workingTime']);
|
||||||
|
try {
|
||||||
|
$repository->insert($model);
|
||||||
|
} catch (RepositoryRecordAlreadyExistException $exception) {
|
||||||
|
throw new HttpConflictRequestException($request, 'Der Eintrag ist schon vorhanden.', $exception);
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
throw new HttpInternalServerErrorException(
|
||||||
|
$request,
|
||||||
|
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank',
|
||||||
|
$exception
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $this->printResponse($response, $model, StatusCodeInterface::STATUS_CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param string $date
|
||||||
|
* @param string $time
|
||||||
|
*
|
||||||
|
* @return WorkingHours
|
||||||
|
* @throws HttpBadRequestException
|
||||||
|
*/
|
||||||
|
protected function buildModel(Request $request, string $date, string $time): WorkingHours
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$workingDay = new DateTime($date);
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
throw new HttpBadRequestException($request, 'Der Wert für das Datum ist falsch', $exception);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$workingTime = new DateInterval('P0000-00-00T' . $time);
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
throw new HttpBadRequestException($request, 'Der Wert für die Zeit ist falsch', $exception);
|
||||||
|
}
|
||||||
|
return (new WorkingHours())->setWorkingDay($workingDay)->setWorkingTime($workingTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Controller;
|
||||||
|
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Fig\Http\Message\StatusCodeInterface;
|
||||||
|
use Slim\Exception\HttpInternalServerErrorException;
|
||||||
|
use Slim\Psr7\Request;
|
||||||
|
use Slim\Psr7\Response;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursMonthlyViewRepository;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursWeeklyViewRepository;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursYearlyViewRepository;
|
||||||
|
|
||||||
|
class WorkingHoursViewController extends AbstractController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array<string, mixed> $args
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*
|
||||||
|
* @throws HttpInternalServerErrorException
|
||||||
|
* @noinspection PhpUnusedParameterInspection
|
||||||
|
*/
|
||||||
|
public function browseWeekly(Request $request, Response $response, array $args): Response
|
||||||
|
{
|
||||||
|
$repository = new WorkingHoursWeeklyViewRepository($this->databases);
|
||||||
|
try {
|
||||||
|
return $this->printResponse($response, $repository->findAll(), StatusCodeInterface::STATUS_OK);
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
throw new HttpInternalServerErrorException(
|
||||||
|
$request,
|
||||||
|
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank', $exception
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array<string, mixed> $args
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*
|
||||||
|
* @throws HttpInternalServerErrorException
|
||||||
|
* @noinspection PhpUnusedParameterInspection
|
||||||
|
*/
|
||||||
|
public function browseMonthly(Request $request, Response $response, array $args): Response
|
||||||
|
{
|
||||||
|
$repository = new WorkingHoursMonthlyViewRepository($this->databases);
|
||||||
|
try {
|
||||||
|
return $this->printResponse($response, $repository->findAll(), StatusCodeInterface::STATUS_OK);
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
throw new HttpInternalServerErrorException(
|
||||||
|
$request,
|
||||||
|
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank', $exception
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array<string, mixed> $args
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*
|
||||||
|
* @throws HttpInternalServerErrorException
|
||||||
|
* @noinspection PhpUnusedParameterInspection
|
||||||
|
*/
|
||||||
|
public function browseYearly(Request $request, Response $response, array $args): Response
|
||||||
|
{
|
||||||
|
$repository = new WorkingHoursYearlyViewRepository($this->databases);
|
||||||
|
try {
|
||||||
|
return $this->printResponse($response, $repository->findAll(), StatusCodeInterface::STATUS_OK);
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
throw new HttpInternalServerErrorException(
|
||||||
|
$request,
|
||||||
|
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank', $exception
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
/** @noinspection PhpUndefinedClassInspection */
|
||||||
|
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Middleware;
|
||||||
|
|
||||||
|
|
||||||
|
use JetBrains\PhpStorm\ArrayShape;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Slim\App;
|
||||||
|
use Slim\Exception\HttpException;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class ErrorHandler
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var App<ContainerInterface>
|
||||||
|
*/
|
||||||
|
protected App $app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ErrorHandler constructor.
|
||||||
|
*
|
||||||
|
* @param App<ContainerInterface> $app
|
||||||
|
*/
|
||||||
|
public function __construct(App $app)
|
||||||
|
{
|
||||||
|
$this->app = $app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Throwable $exception
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
#[ArrayShape([
|
||||||
|
'timestamp' => "false|string",
|
||||||
|
'status' => "int",
|
||||||
|
"error" => "string",
|
||||||
|
'message' => "string",
|
||||||
|
'path' => "string"
|
||||||
|
])] protected function buildJsonArray(Throwable $exception): array
|
||||||
|
{
|
||||||
|
$payload = [
|
||||||
|
'timestamp' => date('Y-m-d\TH:m:sP'),
|
||||||
|
'status' => $exception->getCode(),
|
||||||
|
"error" => '',
|
||||||
|
'message' => $exception->getMessage(),
|
||||||
|
'path' => $exception->getFile()
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($exception instanceof HttpException) {
|
||||||
|
$payload['error'] = $exception->getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param Throwable $exception
|
||||||
|
* @param bool $displayErrorDetails
|
||||||
|
* @param bool $logErrors
|
||||||
|
* @param bool $logErrorDetails
|
||||||
|
* @param LoggerInterface|null $logger
|
||||||
|
*
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function __invoke(
|
||||||
|
ServerRequestInterface $request,
|
||||||
|
Throwable $exception,
|
||||||
|
bool $displayErrorDetails,
|
||||||
|
bool $logErrors,
|
||||||
|
bool $logErrorDetails,
|
||||||
|
?LoggerInterface $logger = null
|
||||||
|
): ResponseInterface {
|
||||||
|
if ($logger !== null && $logErrors === true) {
|
||||||
|
$logger->error($exception->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = $this->buildJsonArray($exception);
|
||||||
|
|
||||||
|
$response = $this->app->getResponseFactory()->createResponse($exception->getCode());
|
||||||
|
$response->getBody()->write(
|
||||||
|
json_encode($payload, JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
$originAccessControlHandler = new OriginAccessControlHandler();
|
||||||
|
|
||||||
|
return $originAccessControlHandler->originAccessControl($request, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Middleware;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
use Psr\Http\Server\MiddlewareInterface;
|
||||||
|
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
|
||||||
|
|
||||||
|
class JsonBodyParserMiddleware implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
public function process(Request $request, RequestHandler $handler): Response
|
||||||
|
{
|
||||||
|
$contentType = $request->getHeaderLine('Content-Type');
|
||||||
|
|
||||||
|
if (str_contains($contentType, 'application/json')) {
|
||||||
|
$contents = json_decode(file_get_contents('php://input'), true);
|
||||||
|
if (json_last_error() === JSON_ERROR_NONE) {
|
||||||
|
$request = $request->withParsedBody($contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $handler->handle($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Middleware;
|
||||||
|
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
use Psr\Http\Server\MiddlewareInterface;
|
||||||
|
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
|
||||||
|
use Slim\Routing\RouteContext;
|
||||||
|
|
||||||
|
class OriginAccessControlHandler implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process an incoming server request.
|
||||||
|
*
|
||||||
|
* Processes an incoming server request in order to produce a response.
|
||||||
|
* If unable to produce the response itself, it may delegate to the provided
|
||||||
|
* request handler to do so.
|
||||||
|
*/
|
||||||
|
public function process(Request $request, RequestHandler $handler): Response
|
||||||
|
{
|
||||||
|
$response = $handler->handle($request);
|
||||||
|
return $this->originAccessControl($request, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function originAccessControl(Request $request, Response $response): Response
|
||||||
|
{
|
||||||
|
$routeContext = RouteContext::fromRequest($request);
|
||||||
|
$routingResults = $routeContext->getRoutingResults();
|
||||||
|
$methods = $routingResults->getAllowedMethods();
|
||||||
|
$requestHeaders = $request->getHeaderLine('Access-Control-Request-Headers');
|
||||||
|
|
||||||
|
if ($this->testRoute($request)) {
|
||||||
|
// Stunden Minuten Sekunden
|
||||||
|
$accessControlMaxAge = 24 * 60 * 60;
|
||||||
|
$response = $response->withHeader('Access-Control-Allow-Origin', $this->buildOrigin($request));
|
||||||
|
$response = $response->withHeader('Access-Control-Allow-Methods', implode(',', $methods));
|
||||||
|
$response = $response->withHeader('Access-Control-Allow-Headers', $requestHeaders);
|
||||||
|
$response = $response->withHeader('Access-Control-Max-Age', (string)$accessControlMaxAge);
|
||||||
|
}
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function testRoute(Request $request): bool
|
||||||
|
{
|
||||||
|
$corsAllowOriginRegex = $_ENV['CORS_ALLOW_ORIGIN_REGEX'] ?? '/https?:\/\/.*:.*/';
|
||||||
|
return preg_match($corsAllowOriginRegex, $this->buildOrigin($request));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildOrigin(Request $request): string
|
||||||
|
{
|
||||||
|
return $request->getHeaderLine('Origin');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +1,16 @@
|
|||||||
<?php /** @noinspection PhpUnusedPrivateFieldInspection */
|
<?php
|
||||||
|
/** @noinspection PhpUnusedPrivateFieldInspection */
|
||||||
|
|
||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Models;
|
namespace TorstenHettstedt\TimekeepingApi\Models;
|
||||||
|
|
||||||
use MyCLabs\Enum\Enum;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum-Klasse für die unterstützten Periode-Definitionen.
|
* Enum-Klasse für die unterstützten Periode-Definitionen.
|
||||||
* Der Wert der Periode ist ein Format-String für {@link https://secure.php.net/manual/en/datetime.format.php}.
|
* Der Wert der Periode ist ein Format-String für {@link https://secure.php.net/manual/en/datetime.format.php}.
|
||||||
*
|
|
||||||
* @extends Enum<string>
|
|
||||||
*
|
|
||||||
* @method static PeriodDesignationEnum WEEKLY()
|
|
||||||
* @method static PeriodDesignationEnum MONTHLY()
|
|
||||||
* @method static PeriodDesignationEnum YEARLY()
|
|
||||||
*/
|
*/
|
||||||
class PeriodDesignationEnum extends Enum
|
enum PeriodDesignationEnum: string
|
||||||
{
|
{
|
||||||
private const WEEKLY = 'Y#W';
|
case WEEKLY = 'Y#W';
|
||||||
private const MONTHLY = 'Y-m';
|
case MONTHLY = 'Y-m';
|
||||||
private const YEARLY = 'Y';
|
case YEARLY = 'Y';
|
||||||
}
|
}
|
||||||
@@ -10,42 +10,26 @@ use JetBrains\PhpStorm\ArrayShape;
|
|||||||
|
|
||||||
class WorkingHours implements ModelInterface
|
class WorkingHours implements ModelInterface
|
||||||
{
|
{
|
||||||
protected ?DateTime $workingDay = null;
|
protected ?DateTime $workingDay = null;
|
||||||
protected ?DateInterval $workingTime = null;
|
protected ?DateInterval $workingTime = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* @return DateTime|null
|
|
||||||
*/
|
|
||||||
public function getWorkingDay(): ?DateTime
|
public function getWorkingDay(): ?DateTime
|
||||||
{
|
{
|
||||||
return $this->workingDay;
|
return $this->workingDay;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function setWorkingDay(DateTime $workingDay): static
|
||||||
* @param DateTime $workingDay
|
|
||||||
*
|
|
||||||
* @return WorkingHours
|
|
||||||
*/
|
|
||||||
public function setWorkingDay(DateTime $workingDay): WorkingHours
|
|
||||||
{
|
{
|
||||||
$this->workingDay = $workingDay;
|
$this->workingDay = $workingDay;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return DateInterval|null
|
|
||||||
*/
|
|
||||||
public function getWorkingTime(): ?DateInterval
|
public function getWorkingTime(): ?DateInterval
|
||||||
{
|
{
|
||||||
return $this->workingTime;
|
return $this->workingTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function setWorkingTime(DateInterval $workingTime): static
|
||||||
* @param DateInterval $workingTime
|
|
||||||
*
|
|
||||||
* @return WorkingHours
|
|
||||||
*/
|
|
||||||
public function setWorkingTime(DateInterval $workingTime): WorkingHours
|
|
||||||
{
|
{
|
||||||
$this->workingTime = $workingTime;
|
$this->workingTime = $workingTime;
|
||||||
return $this;
|
return $this;
|
||||||
@@ -58,7 +42,7 @@ class WorkingHours implements ModelInterface
|
|||||||
public function jsonSerialize(): array
|
public function jsonSerialize(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'workingDay' => $this->getWorkingDay()->format('Y-m-d'),
|
'workingDay' => $this->getWorkingDay()->format('Y-m-d'),
|
||||||
'workingTime' => $this->getWorkingTime()->format('%H:%I:%S')
|
'workingTime' => $this->getWorkingTime()->format('%H:%I:%S')
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,70 +10,36 @@ use JetBrains\PhpStorm\ArrayShape;
|
|||||||
|
|
||||||
class WorkingHoursView implements ModelInterface
|
class WorkingHoursView implements ModelInterface
|
||||||
{
|
{
|
||||||
protected DateTimeInterface $period;
|
|
||||||
protected PeriodDesignationEnum $periodDesignation;
|
|
||||||
protected int $workingDays;
|
|
||||||
protected DateInterval $totalHours;
|
|
||||||
protected DateInterval $overtime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WorkingHoursView constructor.
|
|
||||||
*
|
|
||||||
* @param DateTimeInterface $period
|
|
||||||
* @param PeriodDesignationEnum $periodDesignation
|
|
||||||
* @param int $workingDays
|
|
||||||
* @param DateInterval $totalHours
|
|
||||||
* @param DateInterval $overtime
|
|
||||||
*/
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
DateTimeInterface $period,
|
protected DateTimeInterface $period,
|
||||||
PeriodDesignationEnum $periodDesignation,
|
protected PeriodDesignationEnum $periodDesignation,
|
||||||
int $workingDays,
|
protected int $workingDays,
|
||||||
DateInterval $totalHours,
|
protected DateInterval $totalHours,
|
||||||
DateInterval $overtime
|
protected DateInterval $overtime
|
||||||
) {
|
) {
|
||||||
$this->period = $period;
|
|
||||||
$this->periodDesignation = $periodDesignation;
|
|
||||||
$this->workingDays = $workingDays;
|
|
||||||
$this->totalHours = $totalHours;
|
|
||||||
$this->overtime = $overtime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return DateTimeInterface
|
|
||||||
*/
|
|
||||||
public function getPeriod(): DateTimeInterface
|
public function getPeriod(): DateTimeInterface
|
||||||
{
|
{
|
||||||
return $this->period;
|
return $this->period;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return PeriodDesignationEnum
|
|
||||||
*/
|
|
||||||
public function getPeriodDesignation(): PeriodDesignationEnum
|
public function getPeriodDesignation(): PeriodDesignationEnum
|
||||||
{
|
{
|
||||||
return $this->periodDesignation;
|
return $this->periodDesignation;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getWorkingDays(): int
|
public function getWorkingDays(): int
|
||||||
{
|
{
|
||||||
return $this->workingDays;
|
return $this->workingDays;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return DateInterval
|
|
||||||
*/
|
|
||||||
public function getTotalHours(): DateInterval
|
public function getTotalHours(): DateInterval
|
||||||
{
|
{
|
||||||
return $this->totalHours;
|
return $this->totalHours;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return DateInterval
|
|
||||||
*/
|
|
||||||
public function getOvertime(): DateInterval
|
public function getOvertime(): DateInterval
|
||||||
{
|
{
|
||||||
return $this->overtime;
|
return $this->overtime;
|
||||||
@@ -84,19 +50,22 @@ class WorkingHoursView implements ModelInterface
|
|||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
#[ArrayShape(['period' => "string",
|
#[ArrayShape([
|
||||||
'periodDesignation' => "string",
|
'period' => "string",
|
||||||
'totalHours' => "string",
|
'periodDesignation' => "string",
|
||||||
'workingDays' => "int",
|
'totalHours' => "string",
|
||||||
'overtime' => "string"
|
'workingDays' => "int",
|
||||||
])] public function jsonSerialize(): array
|
'overtime' => "string"
|
||||||
|
])]
|
||||||
|
public function jsonSerialize(): array
|
||||||
{
|
{
|
||||||
|
$formatOvertime = (($this->getOvertime()->invert === 1) ? '-' : '') . '%H:%I:%S';
|
||||||
return [
|
return [
|
||||||
'period' => $this->getPeriod()->format($this->getPeriodDesignation()->getValue()),
|
'period' => $this->getPeriod()->format($this->getPeriodDesignation()->value),
|
||||||
'periodDesignation' => strtolower($this->getPeriodDesignation()->getKey()),
|
'periodDesignation' => strtolower($this->getPeriodDesignation()->name),
|
||||||
'workingDays' => $this->getWorkingDays(),
|
'workingDays' => $this->getWorkingDays(),
|
||||||
'totalHours' => $this->getTotalHours()->format('%H:%I:%S'),
|
'totalHours' => $this->getTotalHours()->format('%H:%I:%S'),
|
||||||
'overtime' => $this->getOvertime()->format('%H:%I:%S')
|
'overtime' => $this->getOvertime()->format($formatOvertime)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,9 +18,8 @@ abstract class AbstractWorkingHoursViewRepository implements RepositoryReaderInt
|
|||||||
{
|
{
|
||||||
|
|
||||||
protected const SQL_SELECT = '';
|
protected const SQL_SELECT = '';
|
||||||
protected const SQL_WHERE = '';
|
protected const SQL_WHERE = '';
|
||||||
|
|
||||||
protected PDO $database;
|
|
||||||
protected PeriodDesignationEnum $periodDesignation;
|
protected PeriodDesignationEnum $periodDesignation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,9 +27,8 @@ abstract class AbstractWorkingHoursViewRepository implements RepositoryReaderInt
|
|||||||
*
|
*
|
||||||
* @param PDO $database
|
* @param PDO $database
|
||||||
*/
|
*/
|
||||||
public function __construct(PDO $database)
|
public function __construct(protected PDO $database)
|
||||||
{
|
{
|
||||||
$this->database = $database;
|
|
||||||
$this->database->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
$this->database->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +49,7 @@ abstract class AbstractWorkingHoursViewRepository implements RepositoryReaderInt
|
|||||||
$model = new WorkingHoursView(
|
$model = new WorkingHoursView(
|
||||||
$this->buildDateFromPeriod($row['period']),
|
$this->buildDateFromPeriod($row['period']),
|
||||||
$this->periodDesignation,
|
$this->periodDesignation,
|
||||||
(int) $row['workingDays'],
|
(int)$row['workingDays'],
|
||||||
$this->buildDateInterval($row['totalHours']),
|
$this->buildDateInterval($row['totalHours']),
|
||||||
$this->buildDateInterval($row['overtime'])
|
$this->buildDateInterval($row['overtime'])
|
||||||
);
|
);
|
||||||
@@ -82,7 +80,7 @@ abstract class AbstractWorkingHoursViewRepository implements RepositoryReaderInt
|
|||||||
return new WorkingHoursView(
|
return new WorkingHoursView(
|
||||||
$this->buildDateFromPeriod($row['period']),
|
$this->buildDateFromPeriod($row['period']),
|
||||||
$this->periodDesignation,
|
$this->periodDesignation,
|
||||||
(int) $row['workingDays'],
|
(int)$row['workingDays'],
|
||||||
$this->buildDateInterval($row['totalHours']),
|
$this->buildDateInterval($row['totalHours']),
|
||||||
$this->buildDateInterval($row['overtime'])
|
$this->buildDateInterval($row['overtime'])
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
namespace TorstenHettstedt\TimekeepingApi\Repositories;
|
namespace TorstenHettstedt\TimekeepingApi\Repositories;
|
||||||
|
|
||||||
|
|
||||||
class RepositoryModelAlreadyExists extends RepositoryException
|
class RepositoryBadWhereDataException extends RepositoryException
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Repositories;
|
namespace TorstenHettstedt\TimekeepingApi\Repositories;
|
||||||
|
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Models\ModelInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T of \TorstenHettstedt\TimekeepingApi\Models\ModelInterface
|
* @template T of ModelInterface
|
||||||
*/
|
*/
|
||||||
interface RepositoryReaderInterface
|
interface RepositoryReaderInterface
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Repositories;
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoryRecordAlreadyExistException extends RepositoryException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,15 +3,17 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Repositories;
|
namespace TorstenHettstedt\TimekeepingApi\Repositories;
|
||||||
|
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Models\ModelInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T of \TorstenHettstedt\TimekeepingApi\Models\ModelInterface
|
* @template T of ModelInterface
|
||||||
*/
|
*/
|
||||||
interface RepositoryWriterInterface
|
interface RepositoryWriterInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param T $model
|
* @param T $model
|
||||||
*
|
*
|
||||||
* @throws RepositoryModelAlreadyExists
|
* @throws RepositoryRecordAlreadyExistException
|
||||||
*/
|
*/
|
||||||
public function insert(mixed $model): void;
|
public function insert(mixed $model): void;
|
||||||
|
|
||||||
|
|||||||
@@ -12,23 +12,20 @@ use TorstenHettstedt\TimekeepingApi\Models\PeriodDesignationEnum;
|
|||||||
class WorkingHoursMonthlyViewRepository extends AbstractWorkingHoursViewRepository
|
class WorkingHoursMonthlyViewRepository extends AbstractWorkingHoursViewRepository
|
||||||
{
|
{
|
||||||
|
|
||||||
protected const SQL_SELECT = <<<SQL
|
protected const string SQL_SELECT = <<<SQL
|
||||||
select
|
select
|
||||||
"Monat" as "period",
|
"Monat" as "period",
|
||||||
"Gesamtarbeitszeit" as "totalHours",
|
"Gesamtarbeitszeit" as "totalHours",
|
||||||
"Arbeitstage" as "workingDays",
|
"Arbeitstage" as "workingDays",
|
||||||
"Überstunden" as "overtime"
|
"Überstunden" as "overtime"
|
||||||
from "Arbeitszeiten - Monat"
|
from "Arbeitszeiten - Monat"
|
||||||
SQL;
|
SQL;
|
||||||
protected const SQL_WHERE = ' where "Monat" = ?';
|
protected const string SQL_WHERE = ' where "Monat" = ?';
|
||||||
|
|
||||||
protected PDO $database;
|
|
||||||
protected PeriodDesignationEnum $periodDesignation;
|
|
||||||
|
|
||||||
public function __construct(PDO $database)
|
public function __construct(PDO $database)
|
||||||
{
|
{
|
||||||
parent::__construct($database);
|
parent::__construct($database);
|
||||||
$this->periodDesignation = PeriodDesignationEnum::MONTHLY();
|
$this->periodDesignation = PeriodDesignationEnum::MONTHLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildDateFromPeriod(string $period): DateTimeInterface
|
protected function buildDateFromPeriod(string $period): DateTimeInterface
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use Exception;
|
|||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use PDO;
|
use PDO;
|
||||||
use PDOException;
|
use PDOException;
|
||||||
|
use PDOStatement;
|
||||||
use TorstenHettstedt\TimekeepingApi\Models\ModelInterface;
|
use TorstenHettstedt\TimekeepingApi\Models\ModelInterface;
|
||||||
use TorstenHettstedt\TimekeepingApi\Models\WorkingHours;
|
use TorstenHettstedt\TimekeepingApi\Models\WorkingHours;
|
||||||
|
|
||||||
@@ -19,17 +20,16 @@ use TorstenHettstedt\TimekeepingApi\Models\WorkingHours;
|
|||||||
*/
|
*/
|
||||||
class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWriterInterface
|
class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWriterInterface
|
||||||
{
|
{
|
||||||
|
protected const string SQL_SELECT = <<<'SQL'
|
||||||
|
select
|
||||||
|
"Datum" as "workingDay",
|
||||||
|
"Arbeitszeit" as "workingTime"
|
||||||
|
from public."Arbeitszeiten"
|
||||||
|
SQL;
|
||||||
|
|
||||||
protected PDO $database;
|
|
||||||
|
|
||||||
/**
|
public function __construct(protected PDO $database)
|
||||||
* WorkingHoursRepository constructor.
|
|
||||||
*
|
|
||||||
* @param PDO $database
|
|
||||||
*/
|
|
||||||
public function __construct(PDO $database)
|
|
||||||
{
|
{
|
||||||
$this->database = $database;
|
|
||||||
$this->database->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
$this->database->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,10 +42,33 @@ class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWri
|
|||||||
* @throws PDOException
|
* @throws PDOException
|
||||||
*/
|
*/
|
||||||
public function findAll(): array
|
public function findAll(): array
|
||||||
|
{
|
||||||
|
return $this->findFiltered(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Gibt alle vorhandenen Einträge zurück
|
||||||
|
*
|
||||||
|
* @param string|null $start
|
||||||
|
* @param string|null $end
|
||||||
|
*
|
||||||
|
* @return WorkingHours[]
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function findFiltered(?string $start, ?string $end): array
|
||||||
{
|
{
|
||||||
$models = [];
|
$models = [];
|
||||||
$stmt = $this->database->prepare('select "Datum" as "workingDay", "Arbeitszeit" as "workingTime" from public."Arbeitszeiten"');
|
$stmt = $this->buildFindFilteredStatement($start, $end);
|
||||||
$stmt->execute();
|
try {
|
||||||
|
$stmt->execute();
|
||||||
|
} catch (PDOException $exception) {
|
||||||
|
if ($exception->getCode() === '22007' || $exception->getCode() === '22008') {
|
||||||
|
throw new RepositoryBadWhereDataException($exception);
|
||||||
|
}
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
|
||||||
while ($row = $stmt->fetch()) {
|
while ($row = $stmt->fetch()) {
|
||||||
$model = new WorkingHours();
|
$model = new WorkingHours();
|
||||||
$model
|
$model
|
||||||
@@ -56,6 +79,35 @@ class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWri
|
|||||||
return $models;
|
return $models;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function buildFilterString(?string $start, ?string $end): string
|
||||||
|
{
|
||||||
|
if (is_string($start) && is_string($end)) {
|
||||||
|
return 'where "Datum" >= :start AND "Datum" <= :end ';
|
||||||
|
}
|
||||||
|
if (is_string($start)) {
|
||||||
|
return 'where "Datum" >= :start ';
|
||||||
|
}
|
||||||
|
if (is_string($end)) {
|
||||||
|
return 'where "Datum" <= :end ';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildFindFilteredStatement(?string $start, ?string $end): ?PDOStatement
|
||||||
|
{
|
||||||
|
$query = static::SQL_SELECT . ' ';
|
||||||
|
$query .= $this->buildFilterString($start, $end);
|
||||||
|
$query .= 'order by "Datum"';
|
||||||
|
$stmt = $this->database->prepare($query);
|
||||||
|
if (is_string($start)) {
|
||||||
|
$stmt->bindParam(':start', $start);
|
||||||
|
}
|
||||||
|
if (is_string($end)) {
|
||||||
|
$stmt->bindParam(':end', $end);
|
||||||
|
}
|
||||||
|
return $stmt ?: null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gibt den Eintrag zurück, der durch die ID
|
* Gibt den Eintrag zurück, der durch die ID
|
||||||
*
|
*
|
||||||
@@ -68,7 +120,7 @@ class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWri
|
|||||||
*/
|
*/
|
||||||
public function findByKey(mixed $primary_key): WorkingHours
|
public function findByKey(mixed $primary_key): WorkingHours
|
||||||
{
|
{
|
||||||
$stmt = $this->database->prepare('select "Datum" as "workingDay", "Arbeitszeit" as "workingTime" from public."Arbeitszeiten" where "Datum" = ?');
|
$stmt = $this->database->prepare(static::SQL_SELECT . ' where "Datum" = ?');
|
||||||
$stmt->bindParam(1, $primary_key);
|
$stmt->bindParam(1, $primary_key);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$row = $stmt->fetch();
|
$row = $stmt->fetch();
|
||||||
@@ -85,7 +137,7 @@ class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWri
|
|||||||
/**
|
/**
|
||||||
* @param WorkingHours|ModelInterface $model
|
* @param WorkingHours|ModelInterface $model
|
||||||
*
|
*
|
||||||
* @throws RepositoryModelAlreadyExists
|
* @throws RepositoryRecordAlreadyExistException
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function insert(mixed $model): void
|
public function insert(mixed $model): void
|
||||||
@@ -99,10 +151,12 @@ class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWri
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$this->findByKey($workingDay);
|
$this->findByKey($workingDay);
|
||||||
throw new RepositoryModelAlreadyExists();
|
throw new RepositoryRecordAlreadyExistException();
|
||||||
} /** @noinspection PhpUnusedLocalVariableInspection */
|
} /** @noinspection PhpUnusedLocalVariableInspection */
|
||||||
catch (RepositoryRecordNotFoundException $exception) {
|
catch (RepositoryRecordNotFoundException $exception) {
|
||||||
$stmt = $this->database->prepare('insert into public."Arbeitszeiten" ("Datum", "Arbeitszeit") values (?, ?) on conflict do nothing');
|
$stmt = $this->database->prepare(
|
||||||
|
'insert into public."Arbeitszeiten" ("Datum", "Arbeitszeit") values (?, ?) on conflict do nothing'
|
||||||
|
);
|
||||||
$stmt->bindParam(1, $workingDay);
|
$stmt->bindParam(1, $workingDay);
|
||||||
$stmt->bindParam(2, $workingTime);
|
$stmt->bindParam(2, $workingTime);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
|
|||||||
@@ -11,23 +11,20 @@ use TorstenHettstedt\TimekeepingApi\Models\PeriodDesignationEnum;
|
|||||||
class WorkingHoursWeeklyViewRepository extends AbstractWorkingHoursViewRepository
|
class WorkingHoursWeeklyViewRepository extends AbstractWorkingHoursViewRepository
|
||||||
{
|
{
|
||||||
|
|
||||||
protected const SQL_SELECT = <<<SQL
|
protected const string SQL_SELECT = <<<SQL
|
||||||
select
|
select
|
||||||
"Woche" as "period",
|
"Woche" as "period",
|
||||||
"Gesamtarbeitszeit" as "totalHours",
|
"Gesamtarbeitszeit" as "totalHours",
|
||||||
"Arbeitstage" as "workingDays",
|
"Arbeitstage" as "workingDays",
|
||||||
"Überstunden" as "overtime"
|
"Überstunden" as "overtime"
|
||||||
from "Arbeitszeiten - Woche"
|
from "Arbeitszeiten - Woche"
|
||||||
SQL;
|
SQL;
|
||||||
protected const SQL_WHERE = ' where "Woche" = ?';
|
protected const string SQL_WHERE = ' where "Woche" = ?';
|
||||||
|
|
||||||
protected PDO $database;
|
|
||||||
protected PeriodDesignationEnum $periodDesignation;
|
|
||||||
|
|
||||||
public function __construct(PDO $database)
|
public function __construct(PDO $database)
|
||||||
{
|
{
|
||||||
parent::__construct($database);
|
parent::__construct($database);
|
||||||
$this->periodDesignation = PeriodDesignationEnum::WEEKLY();
|
$this->periodDesignation = PeriodDesignationEnum::WEEKLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildDateFromPeriod(string $period): DateTimeInterface
|
protected function buildDateFromPeriod(string $period): DateTimeInterface
|
||||||
|
|||||||
@@ -11,23 +11,20 @@ use TorstenHettstedt\TimekeepingApi\Models\PeriodDesignationEnum;
|
|||||||
class WorkingHoursYearlyViewRepository extends AbstractWorkingHoursViewRepository
|
class WorkingHoursYearlyViewRepository extends AbstractWorkingHoursViewRepository
|
||||||
{
|
{
|
||||||
|
|
||||||
protected const SQL_SELECT = <<<SQL
|
protected const string SQL_SELECT = <<<SQL
|
||||||
select
|
select
|
||||||
"Jahr" as "period",
|
"Jahr" as "period",
|
||||||
"Gesamtarbeitszeit" as "totalHours",
|
"Gesamtarbeitszeit" as "totalHours",
|
||||||
"Arbeitstage" as "workingDays",
|
"Arbeitstage" as "workingDays",
|
||||||
"Überstunden" as "overtime"
|
"Überstunden" as "overtime"
|
||||||
from "Arbeitszeiten - Jahr"
|
from "Arbeitszeiten - Jahr"
|
||||||
SQL;
|
SQL;
|
||||||
protected const SQL_WHERE = ' where "Jahr" = ?';
|
protected const string SQL_WHERE = ' where "Jahr" = ?';
|
||||||
|
|
||||||
protected PDO $database;
|
|
||||||
protected PeriodDesignationEnum $periodDesignation;
|
|
||||||
|
|
||||||
public function __construct(PDO $database)
|
public function __construct(PDO $database)
|
||||||
{
|
{
|
||||||
parent::__construct($database);
|
parent::__construct($database);
|
||||||
$this->periodDesignation = PeriodDesignationEnum::YEARLY();
|
$this->periodDesignation = PeriodDesignationEnum::YEARLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+64
-64
@@ -13,10 +13,10 @@ SET xmloption = content;
|
|||||||
SET client_min_messages = warning;
|
SET client_min_messages = warning;
|
||||||
SET row_security = off;
|
SET row_security = off;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS public."Arbeitszeiten";
|
|
||||||
DROP VIEW IF EXISTS public."Arbeitszeiten - Woche";
|
DROP VIEW IF EXISTS public."Arbeitszeiten - Woche";
|
||||||
DROP VIEW IF EXISTS public."Arbeitszeiten - Monat";
|
DROP VIEW IF EXISTS public."Arbeitszeiten - Monat";
|
||||||
DROP VIEW IF EXISTS public."Arbeitszeiten - Jahr";
|
DROP VIEW IF EXISTS public."Arbeitszeiten - Jahr";
|
||||||
|
DROP TABLE IF EXISTS public."Arbeitszeiten";
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: Arbeitszeiten; Type: TABLE; Schema: public; Owner: torsten
|
-- Name: Arbeitszeiten; Type: TABLE; Schema: public; Owner: torsten
|
||||||
@@ -83,66 +83,66 @@ ALTER TABLE public."Arbeitszeiten - Woche" OWNER TO bruce;
|
|||||||
-- Data for Name: Arbeitszeiten; Type: TABLE DATA; Schema: public; Owner: torsten
|
-- Data for Name: Arbeitszeiten; Type: TABLE DATA; Schema: public; Owner: torsten
|
||||||
--
|
--
|
||||||
|
|
||||||
COPY public."Arbeitszeiten" ("Datum", "Arbeitszeit") FROM stdin;
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES
|
||||||
2020-01-07 08:06:00
|
('2020-01-07','08:06:00'),
|
||||||
2020-01-08 08:16:00
|
('2020-01-08','08:16:00'),
|
||||||
2020-01-09 07:49:00
|
('2020-01-09','07:49:00'),
|
||||||
2020-01-10 07:30:00
|
('2020-01-10','07:30:00'),
|
||||||
2020-01-13 08:06:00
|
('2020-01-13','08:06:00'),
|
||||||
2020-01-14 08:04:00
|
('2020-01-14','08:04:00'),
|
||||||
2020-01-15 08:20:00
|
('2020-01-15','08:20:00'),
|
||||||
2020-01-16 07:13:00
|
('2020-01-16','07:13:00'),
|
||||||
2020-01-17 07:57:00
|
('2020-01-17','07:57:00'),
|
||||||
2020-01-20 07:53:00
|
('2020-01-20','07:53:00'),
|
||||||
2020-01-21 07:49:00
|
('2020-01-21','07:49:00'),
|
||||||
2020-01-22 07:56:00
|
('2020-01-22','07:56:00'),
|
||||||
2020-01-23 07:55:00
|
('2020-01-23','07:55:00'),
|
||||||
2020-01-24 07:08:00
|
('2020-01-24','07:08:00'),
|
||||||
2020-01-27 08:23:00
|
('2020-01-27','08:23:00'),
|
||||||
2020-01-28 07:20:00
|
('2020-01-28','07:20:00'),
|
||||||
2020-01-29 08:13:00
|
('2020-01-29','08:13:00'),
|
||||||
2020-01-30 08:43:00
|
('2020-01-30','08:43:00'),
|
||||||
2020-01-31 07:21:00
|
('2020-01-31','07:21:00'),
|
||||||
2020-02-03 07:56:00
|
('2020-02-03','07:56:00'),
|
||||||
2020-02-04 08:05:00
|
('2020-02-04','08:05:00'),
|
||||||
2020-02-05 07:58:00
|
('2020-02-05','07:58:00'),
|
||||||
2020-02-06 08:01:00
|
('2020-02-06','08:01:00'),
|
||||||
2020-02-07 07:59:00
|
('2020-02-07','07:59:00'),
|
||||||
2020-02-10 07:50:00
|
('2020-02-10','07:50:00'),
|
||||||
2020-02-11 08:01:00
|
('2020-02-11','08:01:00'),
|
||||||
2020-02-12 08:14:00
|
('2020-02-12','08:14:00'),
|
||||||
2020-02-13 08:12:00
|
('2020-02-13','08:12:00'),
|
||||||
2020-02-14 07:51:00
|
('2020-02-14','07:51:00'),
|
||||||
2020-02-17 07:59:00
|
('2020-02-17','07:59:00'),
|
||||||
2020-02-18 08:05:00
|
('2020-02-18','08:05:00'),
|
||||||
2020-02-19 07:34:00
|
('2020-02-19','07:34:00'),
|
||||||
2020-02-20 07:33:00
|
('2020-02-20','07:33:00'),
|
||||||
2020-02-21 07:56:00
|
('2020-02-21','07:56:00'),
|
||||||
2020-02-24 08:02:00
|
('2020-02-24','08:02:00'),
|
||||||
2020-02-25 08:13:00
|
('2020-02-25','08:13:00'),
|
||||||
2020-02-26 08:42:00
|
('2020-02-26','08:42:00'),
|
||||||
2020-02-27 07:49:00
|
('2020-02-27','07:49:00'),
|
||||||
2020-02-28 08:16:00
|
('2020-02-28','08:16:00'),
|
||||||
2020-03-02 08:22:00
|
('2020-03-02','08:22:00'),
|
||||||
2020-03-03 08:20:00
|
('2020-03-03','08:20:00'),
|
||||||
2020-03-04 08:12:00
|
('2020-03-04','08:12:00'),
|
||||||
2020-03-05 08:32:00
|
('2020-03-05','08:32:00'),
|
||||||
2020-03-06 04:41:00
|
('2020-03-06','04:41:00'),
|
||||||
2020-03-09 07:05:00
|
('2020-03-09','07:05:00'),
|
||||||
2020-03-10 07:34:00
|
('2020-03-10','07:34:00'),
|
||||||
2020-03-11 07:39:00
|
('2020-03-11','07:39:00'),
|
||||||
2020-03-12 07:50:00
|
('2020-03-12','07:50:00'),
|
||||||
2020-03-13 08:26:00
|
('2020-03-13','08:26:00'),
|
||||||
2020-03-16 07:51:00
|
('2020-03-16','07:51:00'),
|
||||||
2020-03-17 07:50:00
|
('2020-03-17','07:50:00'),
|
||||||
2020-03-18 07:19:00
|
('2020-03-18','07:19:00'),
|
||||||
2020-03-19 07:55:00
|
('2020-03-19','07:55:00'),
|
||||||
2020-03-20 05:43:00
|
('2020-03-20','05:43:00'),
|
||||||
2020-03-23 08:05:00
|
('2020-03-23','08:05:00'),
|
||||||
2020-03-24 08:21:00
|
('2020-03-24','08:21:00'),
|
||||||
2020-03-25 08:10:00
|
('2020-03-25','08:10:00'),
|
||||||
2020-03-26 10:31:00
|
('2020-03-26','10:31:00'),
|
||||||
2020-03-27 04:55:00
|
('2020-03-27','04:55:00'),
|
||||||
2020-03-30 08:21:00
|
('2020-03-30','08:21:00'),
|
||||||
2020-03-31 08:01:00
|
('2020-03-31','08:01:00')
|
||||||
\.
|
;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
namespace Helper;
|
||||||
|
|
||||||
|
// here you can define custom actions
|
||||||
|
// all public methods declared in helper class will be available in $I
|
||||||
|
|
||||||
|
use Codeception\Module;
|
||||||
|
|
||||||
|
class Api extends Module
|
||||||
|
{
|
||||||
|
|
||||||
|
const FORMAT_TIME = 'string:regex(/\d+:[0-5]\d:[0-5]\d/)';
|
||||||
|
const FORMAT_DATE = 'string:regex(/\d{4}-\d{2}-\d{2}/)';
|
||||||
|
|
||||||
|
const WORKING_HOURS_JSON_FORMAT = [
|
||||||
|
'workingDay' => self::FORMAT_DATE,
|
||||||
|
'workingTime' => self::FORMAT_TIME,
|
||||||
|
];
|
||||||
|
|
||||||
|
const WORKING_HOURS_VIEW_JSON_FORMAT = [
|
||||||
|
"period" => "string",
|
||||||
|
"periodDesignation" => "string",
|
||||||
|
"totalHours" => self::FORMAT_TIME,
|
||||||
|
"workingDays" => 'integer:>0',
|
||||||
|
"overtime" => self::FORMAT_TIME,
|
||||||
|
];
|
||||||
|
|
||||||
|
const ERROR_JSON_FORMAT = [
|
||||||
|
"timestamp" => "string:date",
|
||||||
|
"status" => "integer",
|
||||||
|
"error" => "string",
|
||||||
|
"message" => "string",
|
||||||
|
"path" => "string",
|
||||||
|
];
|
||||||
|
|
||||||
|
const TEST_ORIGIN = 'http://localhost:1234';
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
actor: ApiTester
|
||||||
|
modules:
|
||||||
|
enabled:
|
||||||
|
- \Helper\Api
|
||||||
|
- REST:
|
||||||
|
url: http://api:80/
|
||||||
|
depends: PhpBrowser
|
||||||
|
part: Json
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?php /** @noinspection PhpUnused */
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Api\Views;
|
||||||
|
|
||||||
|
use ApiTester;
|
||||||
|
use Codeception\Util\HttpCode;
|
||||||
|
use Helper\Api;
|
||||||
|
|
||||||
|
class BrowseWorkingHoursMonthlyCest
|
||||||
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'GET');
|
||||||
|
$I->sendOptions('/views/working-hours/monthly');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ApiTester $I
|
||||||
|
*/
|
||||||
|
public function browseWorkingHoursView(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendGet('/views/working-hours/monthly');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_VIEW_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?php /** @noinspection PhpUnused */
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Api\Views;
|
||||||
|
|
||||||
|
use ApiTester;
|
||||||
|
use Codeception\Util\HttpCode;
|
||||||
|
use Helper\Api;
|
||||||
|
|
||||||
|
class BrowseWorkingHoursWeeklyCest
|
||||||
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'GET');
|
||||||
|
$I->sendOptions('/views/working-hours/weekly');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ApiTester $I
|
||||||
|
*/
|
||||||
|
public function browseWorkingHoursView(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendGet('/views/working-hours/weekly');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_VIEW_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?php /** @noinspection PhpUnused */
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Api\Views;
|
||||||
|
|
||||||
|
use ApiTester;
|
||||||
|
use Codeception\Util\HttpCode;
|
||||||
|
use Helper\Api;
|
||||||
|
|
||||||
|
class BrowseWorkingHoursYearlyCest
|
||||||
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'GET');
|
||||||
|
$I->sendOptions('/views/working-hours/yearly');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ApiTester $I
|
||||||
|
*/
|
||||||
|
public function browseWorkingHoursView(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendGet('/views/working-hours/yearly');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_VIEW_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<?php /** @noinspection PhpUnused */
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Api\WorkingHours;
|
||||||
|
|
||||||
|
use ApiTester;
|
||||||
|
use Codeception\Example;
|
||||||
|
use Codeception\Util\HttpCode;
|
||||||
|
use Helper\Api;
|
||||||
|
|
||||||
|
class BrowseWorkingHoursCest
|
||||||
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'GET');
|
||||||
|
$I->sendOptions('/working-hours');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ApiTester $I
|
||||||
|
*
|
||||||
|
* @dataProvider validWorkingHoursFilterProvider
|
||||||
|
*/
|
||||||
|
public function browseEmptyWorkingHours(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendGet('/working-hours', ['start-date' => '2020-04-01', 'end-date' => '2020-04-30']);
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseEquals('[]');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validWorkingHoursFilterProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[[null]],
|
||||||
|
[['start-date' => '2020-01-01', 'end-date' => '2020-01-08']],
|
||||||
|
[['start-date' => '2020-01-01', 'end-date' => '2020-04-30']],
|
||||||
|
[['start-date' => '2020-03-01', 'end-date' => '2020-03-07']],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ApiTester $I
|
||||||
|
* @param Example $example
|
||||||
|
*
|
||||||
|
* @dataProvider validWorkingHoursFilterProvider
|
||||||
|
*/
|
||||||
|
public function browseWorkingHours(ApiTester $I, Example $example): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendGet('/working-hours', $example[0]);
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function invalidWorkingHoursFilterProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['2020-11-31'],
|
||||||
|
['2020-11'],
|
||||||
|
['2020'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ApiTester $I
|
||||||
|
* @param Example $example
|
||||||
|
*
|
||||||
|
* @dataProvider invalidWorkingHoursFilterProvider
|
||||||
|
*/
|
||||||
|
public function browseWorkingHoursWithWrongFilterDate(ApiTester $I, Example $example): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendGet('/working-hours', ['start-date' => $example[0]]);
|
||||||
|
$I->seeResponseCodeIs(HttpCode::BAD_REQUEST);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
<?php /** @noinspection PhpUnused */
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Api\WorkingHours;
|
||||||
|
|
||||||
|
use ApiTester;
|
||||||
|
use Codeception\Util\HttpCode;
|
||||||
|
use Helper\Api;
|
||||||
|
|
||||||
|
class CreateWorkingHoursCest
|
||||||
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'POST');
|
||||||
|
$I->sendOptions('/working-hours');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createWorkingHoursWithNewValidRecord(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('accept', 'application/json');
|
||||||
|
$I->haveHttpHeader('content-type', 'application/json');
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendPost('/working-hours', [
|
||||||
|
'workingDay' => '2020-04-01',
|
||||||
|
'workingTime' => '08:00:00',
|
||||||
|
]);
|
||||||
|
$I->seeResponseCodeIs(HttpCode::CREATED);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests
|
||||||
|
public function createWorkingHoursWithNewInvalidRecord(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('accept', 'application/json');
|
||||||
|
$I->haveHttpHeader('content-type', 'application/json');
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendPost('/working-hours', [
|
||||||
|
'workingDay' => '2020-04-01',
|
||||||
|
'workingTime' => 8.0,
|
||||||
|
]);
|
||||||
|
$I->seeResponseCodeIs(HttpCode::BAD_REQUEST);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests
|
||||||
|
public function createWorkingHoursWithExistingValidRecord(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('accept', 'application/json');
|
||||||
|
$I->haveHttpHeader('content-type', 'application/json');
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendPost('/working-hours', [
|
||||||
|
'workingDay' => '2020-01-15',
|
||||||
|
'workingTime' => '08:00:00',
|
||||||
|
]);
|
||||||
|
$I->seeResponseCodeIs(HttpCode::CONFLICT);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php /** @noinspection PhpUnused */
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Api\WorkingHours;
|
||||||
|
|
||||||
|
use ApiTester;
|
||||||
|
use Codeception\Util\HttpCode;
|
||||||
|
use Helper\Api;
|
||||||
|
|
||||||
|
class ReadWorkingHoursCest
|
||||||
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'GET');
|
||||||
|
$I->sendOptions('/working-hours/2020-04-15');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readExistingWorkingHours(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendGet('/working-hours/2020-01-15');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readNotExistingWorkingHours(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendGet('/working-hours/2020-04-15');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::NOT_FOUND);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<?php /** @noinspection PhpUnused */
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Api\WorkingHours;
|
||||||
|
|
||||||
|
use ApiTester;
|
||||||
|
use Codeception\Util\HttpCode;
|
||||||
|
use Helper\Api;
|
||||||
|
|
||||||
|
class UpdateWorkingHoursCest
|
||||||
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'PUT');
|
||||||
|
$I->sendOptions('/working-hours/2020-01-15');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateWorkingHoursWithExistingValidRecord(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('accept', 'application/json');
|
||||||
|
$I->haveHttpHeader('content-type', 'application/json');
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendPut('/working-hours/2020-01-15', [
|
||||||
|
'workingDay' => '2020-01-15',
|
||||||
|
'workingTime' => '10:00:00',
|
||||||
|
]);
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateWorkingHoursWithNewValidRecord(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('accept', 'application/json');
|
||||||
|
$I->haveHttpHeader('content-type', 'application/json');
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendPut('/working-hours/2020-04-15', [
|
||||||
|
'workingDay' => '2020-04-15',
|
||||||
|
'workingTime' => '10:00:00',
|
||||||
|
]);
|
||||||
|
$I->seeResponseCodeIs(HttpCode::NOT_FOUND);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateWorkingHoursWithNewInvalidRecord(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('accept', 'application/json');
|
||||||
|
$I->haveHttpHeader('content-type', 'application/json');
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendPut('/working-hours/2020-01-15', [
|
||||||
|
'workingDay' => '2020-01-15',
|
||||||
|
'workingTime' => 8.0,
|
||||||
|
]);
|
||||||
|
$I->seeResponseCodeIs(HttpCode::BAD_REQUEST);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Controller;
|
||||||
|
|
||||||
|
use Codeception\Test\Unit;
|
||||||
|
use Exception;
|
||||||
|
use PDO;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use Slim\Psr7\Request;
|
||||||
|
use Slim\Psr7\Response;
|
||||||
|
|
||||||
|
class AbstractControllerTest extends Unit
|
||||||
|
{
|
||||||
|
|
||||||
|
protected const FICTIONAL_STATUS_CODE = 666;
|
||||||
|
|
||||||
|
protected ContainerInterface $container;
|
||||||
|
protected Request $request;
|
||||||
|
protected Response|MockObject $response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
* @throws \PHPUnit\Framework\MockObject\Exception
|
||||||
|
*/
|
||||||
|
protected function _before(): void
|
||||||
|
{
|
||||||
|
parent::_before();
|
||||||
|
/** @noinspection SpellCheckingInspection */
|
||||||
|
$this->container = $this->makeEmpty(ContainerInterface::class, [
|
||||||
|
'has' => true,
|
||||||
|
'get' => new PDO(
|
||||||
|
'pgsql:host=psql.torsten-hettstedt.net;port=5432;dbname=testdb;user=bruce;password=mypass'
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
$this->response = $this->createMock(Response::class);
|
||||||
|
$this->response->expects($this->any())->method('getStatusCode')->willReturn(self::FICTIONAL_STATUS_CODE);
|
||||||
|
$this->response->expects($this->any())->method('getBody')->willReturn(
|
||||||
|
$this->makeEmpty(StreamInterface::class, [
|
||||||
|
'write' => function (mixed $data) {
|
||||||
|
$this->assertIsString($data);
|
||||||
|
$this->assertJson($data);
|
||||||
|
return strlen($data);
|
||||||
|
},
|
||||||
|
'getContents' => 'abc',
|
||||||
|
])
|
||||||
|
);
|
||||||
|
$this->response->expects($this->any())->method('withHeader')->willReturn($this->response);
|
||||||
|
$this->response->expects($this->any())->method('withStatus')->willReturn($this->response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,387 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Controller;
|
||||||
|
|
||||||
|
use Codeception\Attribute\DataProvider;
|
||||||
|
use Exception;
|
||||||
|
use Psr\Container\ContainerExceptionInterface;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
|
use Slim\Exception\HttpBadRequestException;
|
||||||
|
use Slim\Exception\HttpNotFoundException;
|
||||||
|
use Slim\Psr7\Request;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Controller\HttpConflictRequestException;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Controller\NotDatabasesException;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Controller\WorkingHoursController;
|
||||||
|
|
||||||
|
class WorkingHoursControllerTest extends AbstractControllerTest
|
||||||
|
{
|
||||||
|
|
||||||
|
protected const int FICTIONAL_STATUS_CODE = 666;
|
||||||
|
public const string EXISTING_DATE = '2020-01-28';
|
||||||
|
public const string EXISTING_INTERVAL = '07:20:00';
|
||||||
|
public const string NEW_DATE = '2020-04-01';
|
||||||
|
public const string NEW_INTERVAL = '07:59:00';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
* @throws \PHPUnit\Framework\MockObject\Exception
|
||||||
|
*/
|
||||||
|
protected function _before(): void
|
||||||
|
{
|
||||||
|
parent::_before();
|
||||||
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
|
'getParsedBody' => [
|
||||||
|
'workingDay' => self::EXISTING_DATE,
|
||||||
|
'workingTime' => self::EXISTING_INTERVAL,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testConstructWithNonDatabase(): void
|
||||||
|
{
|
||||||
|
$this->container = $this->makeEmpty(ContainerInterface::class, [
|
||||||
|
'has' => false,
|
||||||
|
]);
|
||||||
|
$this->expectException(NotDatabasesException::class);
|
||||||
|
new WorkingHoursController($this->container);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, array<int, array<string, string>>>
|
||||||
|
*/
|
||||||
|
public function valideFilterDataProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
[],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-01',
|
||||||
|
'end-date' => '2020-03-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-01',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'end-date' => '2020-03-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string> $queryParams
|
||||||
|
*
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
#[DataProvider('valideFilterDataProvider')]
|
||||||
|
public function testBrowseWithValideParameter(array $queryParams): void
|
||||||
|
{
|
||||||
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
|
'getQueryParams' => $queryParams,
|
||||||
|
]);
|
||||||
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
$response = $controller->browse($this->request, $this->response, []);
|
||||||
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, array<int, array<string, string|null>>>
|
||||||
|
*/
|
||||||
|
public function invalideFilterDataProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '',
|
||||||
|
'end-date' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'end-date' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-01',
|
||||||
|
'end-date' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '',
|
||||||
|
'end-date' => '2020-03-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
//
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-00',
|
||||||
|
'end-date' => '2020-04-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-00',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'end-date' => '2020-04-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-01',
|
||||||
|
'end-date' => '2020-04-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-00',
|
||||||
|
'end-date' => '2020-03-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
//
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03',
|
||||||
|
'end-date' => '2020-03',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'end-date' => '2020-03',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-01',
|
||||||
|
'end-date' => '2020-03',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03',
|
||||||
|
'end-date' => '2020-03-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
//
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020',
|
||||||
|
'end-date' => '2020',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'end-date' => '2020',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-01',
|
||||||
|
'end-date' => '2020',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020',
|
||||||
|
'end-date' => '2020-03-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string> $queryParams
|
||||||
|
*
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
#[DataProvider('invalideFilterDataProvider')]
|
||||||
|
public function testBrowseWithInvalideParameter(array $queryParams): void
|
||||||
|
{
|
||||||
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
|
'getQueryParams' => $queryParams,
|
||||||
|
]);
|
||||||
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
$this->expectException(HttpBadRequestException::class);
|
||||||
|
$controller->browse($this->request, $this->response, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testUpdateExistRecord(): void
|
||||||
|
{
|
||||||
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
$response = $controller->update($this->request, $this->response, [
|
||||||
|
'id' => self::EXISTING_DATE,
|
||||||
|
]);
|
||||||
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testUpdateNotExistRecord(): void
|
||||||
|
{
|
||||||
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
$this->expectException(HttpNotFoundException::class);
|
||||||
|
$controller->update($this->request, $this->response, [
|
||||||
|
'id' => self::NEW_DATE,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[][]
|
||||||
|
*/
|
||||||
|
public function invalidCreatDataProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
self::NEW_DATE,
|
||||||
|
'07:59',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'2020-13-33',
|
||||||
|
self::NEW_INTERVAL,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $date
|
||||||
|
* @param string $time
|
||||||
|
*
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
#[DataProvider('invalidCreatDataProvider')]
|
||||||
|
public function testCreatInvalidData(string $date, string $time): void
|
||||||
|
{
|
||||||
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
|
'getParsedBody' => [
|
||||||
|
'workingDay' => $date,
|
||||||
|
'workingTime' => $time,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
$this->expectException(HttpBadRequestException::class);
|
||||||
|
$controller->creat($this->request, $this->response, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testCreatNewRecord(): void
|
||||||
|
{
|
||||||
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
|
'getParsedBody' => [
|
||||||
|
'workingDay' => self::NEW_DATE,
|
||||||
|
'workingTime' => self::NEW_INTERVAL,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
$response = $controller->creat($this->request, $this->response, []);
|
||||||
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testCreatExistRecord(): void
|
||||||
|
{
|
||||||
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
|
'getParsedBody' => [
|
||||||
|
'workingDay' => self::EXISTING_DATE,
|
||||||
|
'workingTime' => self::EXISTING_INTERVAL,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
$this->expectException(HttpConflictRequestException::class);
|
||||||
|
$controller->creat($this->request, $this->response, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testReadExistRecord(): void
|
||||||
|
{
|
||||||
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
$response = $controller->read($this->request, $this->response, [
|
||||||
|
'id' => self::EXISTING_DATE,
|
||||||
|
]);
|
||||||
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testReadNotExistRecord(): void
|
||||||
|
{
|
||||||
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
$this->expectException(HttpNotFoundException::class);
|
||||||
|
$controller->read($this->request, $this->response, [
|
||||||
|
'id' => self::NEW_DATE,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Controller;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Psr\Container\ContainerExceptionInterface;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
|
use Slim\Psr7\Request;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Controller\NotDatabasesException;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Controller\WorkingHoursViewController;
|
||||||
|
|
||||||
|
class WorkingHoursViewControllerTest extends AbstractControllerTest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
* @throws \PHPUnit\Framework\MockObject\Exception
|
||||||
|
*/
|
||||||
|
protected function _before(): void
|
||||||
|
{
|
||||||
|
parent::_before();
|
||||||
|
$this->request = $this->makeEmpty(Request::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testConstructWithNonDatabase(): void
|
||||||
|
{
|
||||||
|
$this->container = $this->makeEmpty(ContainerInterface::class, [
|
||||||
|
'has' => false
|
||||||
|
]);
|
||||||
|
$this->expectException(NotDatabasesException::class);
|
||||||
|
new WorkingHoursViewController($this->container);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testBrowseMonthly(): void
|
||||||
|
{
|
||||||
|
$controller = new WorkingHoursViewController($this->container);
|
||||||
|
$response = $controller->browseMonthly($this->request, $this->response, []);
|
||||||
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testBrowseYearly(): void
|
||||||
|
{
|
||||||
|
$controller = new WorkingHoursViewController($this->container);
|
||||||
|
$response = $controller->browseYearly($this->request, $this->response, []);
|
||||||
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testBrowseWeekly(): void
|
||||||
|
{
|
||||||
|
$controller = new WorkingHoursViewController($this->container);
|
||||||
|
$response = $controller->browseWeekly($this->request, $this->response, []);
|
||||||
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Middleware;
|
||||||
|
|
||||||
|
use Codeception\Stub\Expected;
|
||||||
|
use Codeception\Test\Unit;
|
||||||
|
use Exception;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Http\Message\ResponseFactoryInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Slim\App;
|
||||||
|
use Slim\Exception\HttpException;
|
||||||
|
use Slim\Interfaces\RouteParserInterface;
|
||||||
|
use Slim\Routing\RouteContext;
|
||||||
|
use Slim\Routing\RoutingResults;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Middleware\ErrorHandler;
|
||||||
|
|
||||||
|
class ErrorHandlerTest extends Unit
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var App<ContainerInterface> */
|
||||||
|
protected App $app;
|
||||||
|
protected ServerRequestInterface $request;
|
||||||
|
protected Exception $exception;
|
||||||
|
protected LoggerInterface $logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function _before(): void
|
||||||
|
{
|
||||||
|
parent::_before();
|
||||||
|
$this->app = $this->makeEmpty(App::class, [
|
||||||
|
'getResponseFactory' => $this->makeEmpty(ResponseFactoryInterface::class, [
|
||||||
|
'createResponse' => $this->makeEmpty(ResponseInterface::class, [
|
||||||
|
'getBody' => $this->makeEmpty(StreamInterface::class, [
|
||||||
|
'write' => function (mixed $data) {
|
||||||
|
$this->assertIsString($data);
|
||||||
|
$this->assertJson($data);
|
||||||
|
return strlen($data);
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
$this->request = $this->makeEmpty(ServerRequestInterface::class, [
|
||||||
|
'getAttribute' => function ($name) {
|
||||||
|
/** @noinspection PhpSwitchCanBeReplacedWithMatchExpressionInspection */
|
||||||
|
switch ($name) {
|
||||||
|
case RouteContext::ROUTE_PARSER:
|
||||||
|
return $this->makeEmpty(RouteParserInterface::class);
|
||||||
|
case RouteContext::ROUTING_RESULTS:
|
||||||
|
return $this->makeEmpty(RoutingResults::class);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testUseWithLogger(): void
|
||||||
|
{
|
||||||
|
$this->exception = $this->make(Exception::class, [
|
||||||
|
'message' => '',
|
||||||
|
'code' => 400,
|
||||||
|
'file' => '/path(to/file',
|
||||||
|
]);
|
||||||
|
$this->logger = $this->makeEmpty(LoggerInterface::class, [
|
||||||
|
'error' => Expected::once(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$middleWare = new ErrorHandler($this->app);
|
||||||
|
$middleWare($this->request, $this->exception, true, true, true, $this->logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testUseWithHttpException(): void
|
||||||
|
{
|
||||||
|
$this->exception = $this->make(HttpException::class, [
|
||||||
|
'message' => '',
|
||||||
|
'code' => 400,
|
||||||
|
'file' => '/path(to/file',
|
||||||
|
'getTitle' => Expected::once('The Title'),
|
||||||
|
]);
|
||||||
|
$this->logger = $this->makeEmpty(LoggerInterface::class);
|
||||||
|
|
||||||
|
$middleWare = new ErrorHandler($this->app);
|
||||||
|
$middleWare($this->request, $this->exception, true, true, true, $this->logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testUseWithGeneralException(): void
|
||||||
|
{
|
||||||
|
$this->exception = $this->make(Exception::class, [
|
||||||
|
'message' => '',
|
||||||
|
'code' => 400,
|
||||||
|
'file' => '/path(to/file',
|
||||||
|
]);
|
||||||
|
$this->logger = $this->makeEmpty(LoggerInterface::class);
|
||||||
|
|
||||||
|
$middleWare = new ErrorHandler($this->app);
|
||||||
|
$middleWare($this->request, $this->exception, true, true, true, $this->logger);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Models;
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Models;
|
||||||
|
|
||||||
|
use Codeception\Attribute\DataProvider;
|
||||||
use Codeception\Test\Unit;
|
use Codeception\Test\Unit;
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
@@ -22,7 +23,7 @@ class WorkingHoursTest extends Unit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array[]
|
* @return array<array<DateTime|DateInterval|array<string,string>>>
|
||||||
*/
|
*/
|
||||||
public function workingHoursProvider(): array
|
public function workingHoursProvider(): array
|
||||||
{
|
{
|
||||||
@@ -49,17 +50,13 @@ class WorkingHoursTest extends Unit
|
|||||||
/**
|
/**
|
||||||
* @param DateTime $date
|
* @param DateTime $date
|
||||||
* @param DateInterval $interval
|
* @param DateInterval $interval
|
||||||
* @param array<string, string> $should_json
|
* @param array<string, string> $shouldJson
|
||||||
*
|
|
||||||
* @dataProvider workingHoursProvider
|
|
||||||
*/
|
*/
|
||||||
public function testWorkingHoursWithContent(DateTime $date, DateInterval $interval, array $should_json): void
|
#[DataProvider('workingHoursProvider')]
|
||||||
|
public function testWorkingHoursWithContent(DateTime $date, DateInterval $interval, array $shouldJson): void
|
||||||
{
|
{
|
||||||
|
|
||||||
$obj = new WorkingHours();
|
$obj = new WorkingHours();
|
||||||
$obj
|
$obj->setWorkingDay($date)->setWorkingTime($interval);
|
||||||
->setWorkingDay($date)
|
|
||||||
->setWorkingTime($interval);
|
|
||||||
|
|
||||||
$this->assertNotNull($obj->getWorkingDay());
|
$this->assertNotNull($obj->getWorkingDay());
|
||||||
$this->assertInstanceOf(DateTime::class, $obj->getWorkingDay());
|
$this->assertInstanceOf(DateTime::class, $obj->getWorkingDay());
|
||||||
@@ -70,7 +67,6 @@ class WorkingHoursTest extends Unit
|
|||||||
$this->assertEquals($interval, $obj->getWorkingTime());
|
$this->assertEquals($interval, $obj->getWorkingTime());
|
||||||
|
|
||||||
$json = $obj->jsonSerialize();
|
$json = $obj->jsonSerialize();
|
||||||
$this->assertIsArray($json);
|
$this->assertEquals($shouldJson, $json);
|
||||||
$this->assertEquals($should_json, $json);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Models;
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Models;
|
||||||
|
|
||||||
|
use Codeception\Attribute\DataProvider;
|
||||||
use Codeception\Test\Unit;
|
use Codeception\Test\Unit;
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
@@ -13,17 +14,23 @@ class WorkingHoursViewTest extends Unit
|
|||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array[]
|
* @return array<array{DateTime,PeriodDesignationEnum,int,DateInterval,DateInterval,array<string,mixed>}>
|
||||||
*/
|
*/
|
||||||
public function workingHoursProvider(): array
|
public function workingHoursProvider(): array
|
||||||
{
|
{
|
||||||
|
$date = new DateTime('2020-11-30');
|
||||||
|
$work_days = 15;
|
||||||
|
$total_hours = new DateInterval("PT32H22M");
|
||||||
|
$overtime = new DateInterval("PT22M");
|
||||||
|
$absence_time = clone $overtime;
|
||||||
|
$absence_time->invert = 1;
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
new DateTime('2020-11-30'),
|
$date,
|
||||||
PeriodDesignationEnum::WEEKLY(),
|
PeriodDesignationEnum::WEEKLY,
|
||||||
15,
|
$work_days,
|
||||||
new DateInterval("PT32H22M"),
|
$total_hours,
|
||||||
new DateInterval("PT22M"),
|
$overtime,
|
||||||
[
|
[
|
||||||
'period' => '2020#49',
|
'period' => '2020#49',
|
||||||
'periodDesignation' => 'weekly',
|
'periodDesignation' => 'weekly',
|
||||||
@@ -33,11 +40,25 @@ class WorkingHoursViewTest extends Unit
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
new DateTime('2020-11-30'),
|
$date,
|
||||||
PeriodDesignationEnum::MONTHLY(),
|
PeriodDesignationEnum::WEEKLY,
|
||||||
15,
|
$work_days,
|
||||||
new DateInterval("PT32H22M"),
|
$total_hours,
|
||||||
new DateInterval("PT22M"),
|
$absence_time,
|
||||||
|
[
|
||||||
|
'period' => '2020#49',
|
||||||
|
'periodDesignation' => 'weekly',
|
||||||
|
'workingDays' => 15,
|
||||||
|
'totalHours' => '32:22:00',
|
||||||
|
'overtime' => '-00:22:00'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
$date,
|
||||||
|
PeriodDesignationEnum::MONTHLY,
|
||||||
|
$work_days,
|
||||||
|
$total_hours,
|
||||||
|
$overtime,
|
||||||
[
|
[
|
||||||
'period' => '2020-11',
|
'period' => '2020-11',
|
||||||
'periodDesignation' => 'monthly',
|
'periodDesignation' => 'monthly',
|
||||||
@@ -47,11 +68,25 @@ class WorkingHoursViewTest extends Unit
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
new DateTime('2020-11-30'),
|
$date,
|
||||||
PeriodDesignationEnum::YEARLY(),
|
PeriodDesignationEnum::MONTHLY,
|
||||||
15,
|
$work_days,
|
||||||
new DateInterval("PT32H22M"),
|
$total_hours,
|
||||||
new DateInterval("PT22M"),
|
$absence_time,
|
||||||
|
[
|
||||||
|
'period' => '2020-11',
|
||||||
|
'periodDesignation' => 'monthly',
|
||||||
|
'workingDays' => 15,
|
||||||
|
'totalHours' => '32:22:00',
|
||||||
|
'overtime' => '-00:22:00'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
$date,
|
||||||
|
PeriodDesignationEnum::YEARLY,
|
||||||
|
$work_days,
|
||||||
|
$total_hours,
|
||||||
|
$overtime,
|
||||||
[
|
[
|
||||||
'period' => '2020',
|
'period' => '2020',
|
||||||
'periodDesignation' => 'yearly',
|
'periodDesignation' => 'yearly',
|
||||||
@@ -60,54 +95,58 @@ class WorkingHoursViewTest extends Unit
|
|||||||
'overtime' => '00:22:00'
|
'overtime' => '00:22:00'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
$date,
|
||||||
|
PeriodDesignationEnum::YEARLY,
|
||||||
|
$work_days,
|
||||||
|
$total_hours,
|
||||||
|
$absence_time,
|
||||||
|
[
|
||||||
|
'period' => '2020',
|
||||||
|
'periodDesignation' => 'yearly',
|
||||||
|
'workingDays' => 15,
|
||||||
|
'totalHours' => '32:22:00',
|
||||||
|
'overtime' => '-00:22:00'
|
||||||
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param DateTimeInterface $period
|
* @param DateTimeInterface $period
|
||||||
* @param PeriodDesignationEnum $periodDesignation
|
* @param PeriodDesignationEnum $periodDesignation
|
||||||
* @param int $workingDays
|
* @param int $workingDays
|
||||||
* @param DateInterval $totalHours
|
* @param DateInterval $totalHours
|
||||||
* @param DateInterval $overtime
|
* @param DateInterval $overtime
|
||||||
* @param array<string, string> $should_json
|
* @param array<string, string> $shouldJson
|
||||||
*
|
|
||||||
* @dataProvider workingHoursProvider
|
|
||||||
*/
|
*/
|
||||||
|
#[DataProvider('workingHoursProvider')]
|
||||||
public function testWorkingHoursViewWithContent(
|
public function testWorkingHoursViewWithContent(
|
||||||
DateTimeInterface $period,
|
DateTimeInterface $period,
|
||||||
PeriodDesignationEnum $periodDesignation,
|
PeriodDesignationEnum $periodDesignation,
|
||||||
int $workingDays,
|
int $workingDays,
|
||||||
DateInterval $totalHours,
|
DateInterval $totalHours,
|
||||||
DateInterval $overtime,
|
DateInterval $overtime,
|
||||||
array $should_json
|
array $shouldJson
|
||||||
): void
|
): void {
|
||||||
{
|
|
||||||
|
|
||||||
$obj = new WorkingHoursView($period, $periodDesignation, $workingDays, $totalHours, $overtime);
|
$obj = new WorkingHoursView($period, $periodDesignation, $workingDays, $totalHours, $overtime);
|
||||||
|
|
||||||
$this->assertNotNull($obj->getPeriod());
|
|
||||||
$this->assertInstanceOf(DateTimeInterface::class, $obj->getPeriod());
|
$this->assertInstanceOf(DateTimeInterface::class, $obj->getPeriod());
|
||||||
$this->assertEquals($period, $obj->getPeriod());
|
$this->assertEquals($period, $obj->getPeriod());
|
||||||
|
|
||||||
$this->assertNotNull($obj->getPeriodDesignation());
|
|
||||||
$this->assertInstanceOf(PeriodDesignationEnum::class, $obj->getPeriodDesignation());
|
$this->assertInstanceOf(PeriodDesignationEnum::class, $obj->getPeriodDesignation());
|
||||||
$this->assertEquals($periodDesignation, $obj->getPeriodDesignation());
|
$this->assertEquals($periodDesignation, $obj->getPeriodDesignation());
|
||||||
|
|
||||||
$this->assertNotNull($obj->getWorkingDays());
|
|
||||||
$this->assertIsInt($obj->getWorkingDays());
|
|
||||||
$this->assertEquals($workingDays, $obj->getWorkingDays());
|
$this->assertEquals($workingDays, $obj->getWorkingDays());
|
||||||
|
|
||||||
$this->assertNotNull($obj->getTotalHours());
|
|
||||||
$this->assertInstanceOf(DateInterval::class, $obj->getTotalHours());
|
$this->assertInstanceOf(DateInterval::class, $obj->getTotalHours());
|
||||||
$this->assertEquals($totalHours, $obj->getTotalHours());
|
$this->assertEquals($totalHours, $obj->getTotalHours());
|
||||||
|
|
||||||
$this->assertNotNull($obj->getOvertime());
|
|
||||||
$this->assertInstanceOf(DateInterval::class, $obj->getOvertime());
|
$this->assertInstanceOf(DateInterval::class, $obj->getOvertime());
|
||||||
$this->assertEquals($overtime, $obj->getOvertime());
|
$this->assertEquals($overtime, $obj->getOvertime());
|
||||||
|
|
||||||
|
|
||||||
$json = $obj->jsonSerialize();
|
$json = $obj->jsonSerialize();
|
||||||
$this->assertIsArray($json);
|
$this->assertEquals($shouldJson, $json);
|
||||||
$this->assertEquals($should_json, $json);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,49 +2,124 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Repositories;
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Repositories;
|
||||||
|
|
||||||
|
use Codeception\Attribute\DataProvider;
|
||||||
use Codeception\Test\Unit;
|
use Codeception\Test\Unit;
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use Exception;
|
use Exception;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use PDO;
|
use PDO;
|
||||||
use PDOStatement;
|
|
||||||
use TorstenHettstedt\TimekeepingApi\Models\ModelInterface;
|
use TorstenHettstedt\TimekeepingApi\Models\ModelInterface;
|
||||||
use TorstenHettstedt\TimekeepingApi\Models\WorkingHours;
|
use TorstenHettstedt\TimekeepingApi\Models\WorkingHours;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryModelAlreadyExists;
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryBadWhereDataException;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordAlreadyExistException;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordNotFoundException;
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordNotFoundException;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursRepository;
|
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursRepository;
|
||||||
|
|
||||||
class WorkingHoursRepositoryTest extends Unit
|
class WorkingHoursRepositoryTest extends Unit
|
||||||
{
|
{
|
||||||
const EXISTING_DATE = '2020-01-28';
|
public const string EXISTING_DATE = '2020-01-28';
|
||||||
const EXISTING_INTERVAL = 'PT7H20M';
|
public const string EXISTING_INTERVAL = 'PT7H20M';
|
||||||
const NEW_DATE = '2020-04-01';
|
public const string NEW_DATE = '2020-04-01';
|
||||||
const NEW_INTERVAL = 'PT7H59M';
|
public const string NEW_INTERVAL = 'PT7H59M';
|
||||||
const RECORDS_COUNT = 61;
|
public const int RECORDS_COUNT = 61;
|
||||||
|
|
||||||
protected PDO $pdoObject;
|
protected PDO $pdoObject;
|
||||||
protected PDOStatement $pdoStatement;
|
|
||||||
|
|
||||||
protected function _before(): void
|
protected function _before(): void
|
||||||
{
|
{
|
||||||
/** @noinspection SpellCheckingInspection */
|
/** @noinspection SpellCheckingInspection */
|
||||||
$this->pdoObject = new PDO('pgsql:host=psql.torsten-hettstedt.net;port=5432;dbname=testdb;user=bruce;password=mypass');
|
$this->pdoObject = new PDO(
|
||||||
|
'pgsql:host=psql.torsten-hettstedt.net;port=5432;dbname=testdb;user=bruce;password=mypass'
|
||||||
|
);
|
||||||
parent::_before();
|
parent::_before();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testFindAll(): void
|
public function testFindAllWithBlankParameter(): void
|
||||||
{
|
{
|
||||||
$repository = new WorkingHoursRepository($this->pdoObject);
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
$models = $repository->findAll();
|
$models = $repository->findAll();
|
||||||
$this->assertIsArray($models);
|
|
||||||
$this->assertContainsOnly(WorkingHours::class, $models);
|
$this->assertContainsOnlyInstancesOf(WorkingHours::class, $models);
|
||||||
$this->assertCount(self::RECORDS_COUNT, $models);
|
$this->assertCount(self::RECORDS_COUNT, $models);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, array<int|string|null>>
|
||||||
|
*/
|
||||||
|
public function valideFilterParameter(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[null, null, 61,],
|
||||||
|
['2020-04-01', null, 0,],
|
||||||
|
['2020-04-01', '2020-04-30', 0,],
|
||||||
|
[null, '2020-04-30', 61,],
|
||||||
|
['2020-03-01', null, 22,],
|
||||||
|
['2020-03-01', '2020-03-31', 22,],
|
||||||
|
['2020-03-01', '2020-03-07', 5,],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $start
|
||||||
|
* @param string|null $ende
|
||||||
|
* @param int $count
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
#[DataProvider('valideFilterParameter')]
|
||||||
|
public function testFindAllWithValideParameter(?string $start, ?string $ende, int $count): void
|
||||||
|
{
|
||||||
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
|
$models = $repository->findFiltered($start, $ende);
|
||||||
|
|
||||||
|
$this->assertContainsOnlyInstancesOf(WorkingHours::class, $models);
|
||||||
|
$this->assertCount($count, $models);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, array<int|string|null>>
|
||||||
|
*/
|
||||||
|
public function invalideFilterParameter(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['2020-04-31', null],
|
||||||
|
[null, '2020-04-31'],
|
||||||
|
['2020-04-00', '2020-04-31'],
|
||||||
|
['2020-04-01', '2020-04-31'],
|
||||||
|
['2020-04-00', '2020-04-30'],
|
||||||
|
//
|
||||||
|
['2020-04', null],
|
||||||
|
[null, '2020-04'],
|
||||||
|
['2020-04', '2020-04'],
|
||||||
|
['2020-04-01', '2020-04'],
|
||||||
|
['2020-04', '2020-04-30'],
|
||||||
|
//
|
||||||
|
['2020', null],
|
||||||
|
[null, '2020'],
|
||||||
|
['2020', '2020'],
|
||||||
|
['2020-04-01', '2020'],
|
||||||
|
['2020', '2020-04-30'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $start
|
||||||
|
* @param string|null $ende
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
#[DataProvider('invalideFilterParameter')]
|
||||||
|
public function testFindAllWithInvalideParameter(?string $start, ?string $ende): void
|
||||||
|
{
|
||||||
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
|
$this->expectException(RepositoryBadWhereDataException::class);
|
||||||
|
$repository->findFiltered($start, $ende);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws RepositoryRecordNotFoundException
|
* @throws RepositoryRecordNotFoundException
|
||||||
*/
|
*/
|
||||||
@@ -52,7 +127,6 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
{
|
{
|
||||||
$repository = new WorkingHoursRepository($this->pdoObject);
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
$model = $repository->findByKey(self::EXISTING_DATE);
|
$model = $repository->findByKey(self::EXISTING_DATE);
|
||||||
$this->assertInstanceOf(WorkingHours::class, $model);
|
|
||||||
$this->assertEquals(new DateTime(self::EXISTING_DATE), $model->getWorkingDay());
|
$this->assertEquals(new DateTime(self::EXISTING_DATE), $model->getWorkingDay());
|
||||||
$this->assertEquals(new DateInterval(self::EXISTING_INTERVAL), $model->getWorkingTime());
|
$this->assertEquals(new DateInterval(self::EXISTING_INTERVAL), $model->getWorkingTime());
|
||||||
}
|
}
|
||||||
@@ -88,7 +162,6 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
]);
|
]);
|
||||||
$repository->insert($model);
|
$repository->insert($model);
|
||||||
$model = $repository->findByKey(self::NEW_DATE);
|
$model = $repository->findByKey(self::NEW_DATE);
|
||||||
$this->assertInstanceOf(WorkingHours::class, $model);
|
|
||||||
$this->assertEquals(new DateTime(self::NEW_DATE), $model->getWorkingDay());
|
$this->assertEquals(new DateTime(self::NEW_DATE), $model->getWorkingDay());
|
||||||
$this->assertEquals(new DateInterval(self::NEW_INTERVAL), $model->getWorkingTime());
|
$this->assertEquals(new DateInterval(self::NEW_INTERVAL), $model->getWorkingTime());
|
||||||
$this->assertCount(self::RECORDS_COUNT + 1, $repository->findAll());
|
$this->assertCount(self::RECORDS_COUNT + 1, $repository->findAll());
|
||||||
@@ -104,7 +177,7 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
'workingDay' => new DateTime(self::EXISTING_DATE),
|
'workingDay' => new DateTime(self::EXISTING_DATE),
|
||||||
'workingTime' => new DateInterval(self::NEW_INTERVAL),
|
'workingTime' => new DateInterval(self::NEW_INTERVAL),
|
||||||
]);
|
]);
|
||||||
$this->expectException(RepositoryModelAlreadyExists::class);
|
$this->expectException(RepositoryRecordAlreadyExistException::class);
|
||||||
$repository->insert($model);
|
$repository->insert($model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Repositories;
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Repositories;
|
||||||
|
|
||||||
|
use Codeception\Attribute\DataProvider;
|
||||||
use Codeception\Test\Unit;
|
use Codeception\Test\Unit;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use DateTimeZone;
|
use DateTimeZone;
|
||||||
use Exception;
|
use PDO;use TorstenHettstedt\TimekeepingApi\Models\WorkingHoursView;
|
||||||
use PDO;
|
|
||||||
use TorstenHettstedt\TimekeepingApi\Models\WorkingHoursView;
|
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordNotFoundException;
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordNotFoundException;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursWeeklyViewRepository;
|
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursWeeklyViewRepository;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursMonthlyViewRepository;
|
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursMonthlyViewRepository;
|
||||||
@@ -25,7 +24,7 @@ class WorkingHoursViewRepositoryTest extends Unit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array[]
|
* @return array<array{string, int}>
|
||||||
*/
|
*/
|
||||||
public function listObjectProvider(): array
|
public function listObjectProvider(): array
|
||||||
{
|
{
|
||||||
@@ -37,20 +36,19 @@ class WorkingHoursViewRepositoryTest extends Unit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array[]
|
* @return array<array{string, string, int, DateTime}>
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public function existingObjectProvider(): array
|
public function existingObjectProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[WorkingHoursYearlyViewRepository::class, '2020', 61, new DateTime('2020-01-01', new DateTimeZone('UCT'))],
|
[WorkingHoursYearlyViewRepository::class, '2020', 61, date_create('2020-01-01', new DateTimeZone('UCT'))],
|
||||||
[WorkingHoursMonthlyViewRepository::class, '2020 February', 20, new DateTime('2020-02-01', new DateTimeZone('UCT'))],
|
[WorkingHoursMonthlyViewRepository::class, '2020 February', 20, date_create('2020-02-01', new DateTimeZone('UCT'))],
|
||||||
[WorkingHoursWeeklyViewRepository::class, '2020#03', 5, new DateTime('2020-01-13', new DateTimeZone('UCT'))],
|
[WorkingHoursWeeklyViewRepository::class, '2020#03', 5, date_create('2020-01-13', new DateTimeZone('UCT'))],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array[]
|
* @return array<array{string, string}>
|
||||||
*/
|
*/
|
||||||
public function notExistingObjectProvider(): array
|
public function notExistingObjectProvider(): array
|
||||||
{
|
{
|
||||||
@@ -61,49 +59,30 @@ class WorkingHoursViewRepositoryTest extends Unit
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#[DataProvider(('listObjectProvider'))]
|
||||||
* @param string $period_class
|
public function testFindAll(string $periodClass, int $countRecords): void
|
||||||
* @param int $count_records
|
|
||||||
*
|
|
||||||
* @dataProvider listObjectProvider
|
|
||||||
*/
|
|
||||||
public function testFindAll(string $period_class, int $count_records): void
|
|
||||||
{
|
{
|
||||||
$repository = new $period_class($this->pdoObject);
|
$repository = new $periodClass($this->pdoObject);
|
||||||
$models = $repository->findAll();
|
$models = $repository->findAll();
|
||||||
$this->assertIsArray($models);
|
$this->assertIsArray($models);
|
||||||
$this->assertContainsOnly(WorkingHoursView::class, $models);
|
$this->assertContainsOnlyInstancesOf(WorkingHoursView::class, $models);
|
||||||
$this->assertCount($count_records, $models);
|
$this->assertCount($countRecords, $models);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#[DataProvider(('existingObjectProvider'))]
|
||||||
*
|
public function testExistingFindByKey(string $periodClass, string $search, int $workingDays, DateTime $period): void
|
||||||
* @dataProvider existingObjectProvider
|
|
||||||
*
|
|
||||||
* @param string $period_class
|
|
||||||
* @param string $search
|
|
||||||
* @param int $workingDays
|
|
||||||
* @param DateTime $period
|
|
||||||
*/
|
|
||||||
public function testExistingFindByKey(string $period_class, string $search, int $workingDays, DateTime $period): void
|
|
||||||
{
|
{
|
||||||
$repository = new $period_class($this->pdoObject);
|
$repository = new $periodClass($this->pdoObject);
|
||||||
$model = $repository->findByKey($search);
|
$model = $repository->findByKey($search);
|
||||||
$this->assertInstanceOf(WorkingHoursView::class, $model);
|
$this->assertInstanceOf(WorkingHoursView::class, $model);
|
||||||
$this->assertEquals($workingDays, $model->getWorkingDays());
|
$this->assertEquals($workingDays, $model->getWorkingDays());
|
||||||
$this->assertEquals($period->format('Ymd'), $model->getPeriod()->format('Ymd'));
|
$this->assertEquals($period->format('Ymd'), $model->getPeriod()->format('Ymd'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#[DataProvider(('notExistingObjectProvider'))]
|
||||||
* @param string $period_class
|
public function testNotExistingFindByKey(string $periodClass, string $search): void
|
||||||
* @param string $search
|
|
||||||
*
|
|
||||||
* @dataProvider notExistingObjectProvider
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function testNotExistingFindByKey(string $period_class, string $search): void
|
|
||||||
{
|
{
|
||||||
$repository = new $period_class($this->pdoObject);
|
$repository = new $periodClass($this->pdoObject);
|
||||||
$this->expectException(RepositoryRecordNotFoundException::class);
|
$this->expectException(RepositoryRecordNotFoundException::class);
|
||||||
$repository->findByKey($search);
|
$repository->findByKey($search);
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-11
@@ -1,13 +1,35 @@
|
|||||||
volumes:
|
volumes:
|
||||||
logs:
|
logs:
|
||||||
driver: local
|
driver: local
|
||||||
services:
|
services:
|
||||||
api:
|
api:
|
||||||
build: ./api/
|
build: ./api/
|
||||||
environment:
|
environment:
|
||||||
docker: "true"
|
docker: "true"
|
||||||
ports:
|
ports:
|
||||||
- 8090:80
|
- "8090:80"
|
||||||
volumes:
|
volumes:
|
||||||
- ./api:/var/www
|
- ./api:/var/www
|
||||||
- logs:/var/www/logs
|
- logs:/var/www/logs
|
||||||
|
test-api:
|
||||||
|
build: ./api/
|
||||||
|
environment:
|
||||||
|
docker: "true"
|
||||||
|
DATABASES_HOST: psql.torsten-hettstedt.net
|
||||||
|
DATABASES_NAME: testdb
|
||||||
|
DATABASES_USER: bruce
|
||||||
|
DATABASES_PASS: mypass
|
||||||
|
ports:
|
||||||
|
- "8091:80"
|
||||||
|
volumes:
|
||||||
|
- ./api:/var/www
|
||||||
|
- logs:/var/www/logs
|
||||||
|
ui:
|
||||||
|
build: ./ui/
|
||||||
|
env_file:
|
||||||
|
- ui/.env
|
||||||
|
ports:
|
||||||
|
- "8080:3000"
|
||||||
|
depends_on:
|
||||||
|
- api
|
||||||
|
- test-api
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
create_css_files() {
|
||||||
|
cd ui || exit 2
|
||||||
|
lessc --source-map less/print.less static/print.css
|
||||||
|
lessc --source-map less/global.less static/global.css
|
||||||
|
cd ../..
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
install_ui_dependence() {
|
||||||
|
cd ui/ || exit 2
|
||||||
|
npm install
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
install_api_dependence() {
|
||||||
|
cd api/ || exit 2
|
||||||
|
composer install
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
build_ui() {
|
||||||
|
cd ui/ || exit 2
|
||||||
|
npm run build
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
codeception_install() {
|
||||||
|
pwd
|
||||||
|
cd api/ || exit 2
|
||||||
|
test -e ./tests/unit.suite.yml || ./vendor/bin/codecept build
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
test_unit() {
|
||||||
|
pwd
|
||||||
|
cd api/ || exit 2
|
||||||
|
./vendor/bin/codecept run unit
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
test_api() {
|
||||||
|
cd api/ || exit 2
|
||||||
|
./vendor/bin/codecept run api
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
composer_run() {
|
||||||
|
docker compose up -d
|
||||||
|
}
|
||||||
|
|
||||||
|
create_css_files && install_ui_dependence && install_api_dependence && build_ui && codeception_install && test_unit && composer_run && test_api
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
@startuml
|
||||||
|
'https://plantuml.com/salt
|
||||||
|
|
||||||
|
salt
|
||||||
|
{
|
||||||
|
{/ <b>Täglich | Wöchentlich }
|
||||||
|
Datum |"0000-00-00 <&calendar>"
|
||||||
|
Uhrzeit | "00:00:00 <&clock>"
|
||||||
|
[Übernehmen und weiter <&plus>]
|
||||||
|
[Abbrechen <&action-undo>]
|
||||||
|
}
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
@startuml
|
||||||
|
'https://plantuml.com/salt
|
||||||
|
|
||||||
|
salt
|
||||||
|
{
|
||||||
|
{/ Täglich | <b>Wöchentlich }
|
||||||
|
Woche vom
|
||||||
|
"0000-00-00 <&calendar>"
|
||||||
|
[<&chevron-left> Woche zurück] | [Woche vor <&chevron-right>]
|
||||||
|
{!
|
||||||
|
Montag | "00:00:00 <&clock>"
|
||||||
|
Dienstag | "00:00:00 <&clock>"
|
||||||
|
Mittwoch | "00:00:00 <&clock>"
|
||||||
|
Donnerstag | "00:00:00 <&clock>"
|
||||||
|
Freitag | "00:00:00 <&clock>"
|
||||||
|
<color:red>Samstag | "<color:red>00:00:00 <&clock>"
|
||||||
|
<color:red>Sontag | "<color:red>00:00:00 <&clock>"
|
||||||
|
}
|
||||||
|
[Übernehmen und weiter <&plus>]
|
||||||
|
[Abbrechen <&action-undo>]
|
||||||
|
}
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,442 @@
|
|||||||
|
truncate table public."Arbeitszeiten";
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-09', '0 years 0 mons 0 days 8 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-10', '0 years 0 mons 0 days 8 hours 23 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-11', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-14', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-15', '0 years 0 mons 0 days 9 hours 11 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-16', '0 years 0 mons 0 days 8 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-17', '0 years 0 mons 0 days 9 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-18', '0 years 0 mons 0 days 7 hours 31 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-21', '0 years 0 mons 0 days 8 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-22', '0 years 0 mons 0 days 8 hours 31 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-23', '0 years 0 mons 0 days 9 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-24', '0 years 0 mons 0 days 7 hours 17 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-25', '0 years 0 mons 0 days 7 hours 22 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-04', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-05', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-06', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-07', '0 years 0 mons 0 days 7 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-08', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-11', '0 years 0 mons 0 days 6 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-12', '0 years 0 mons 0 days 8 hours 48 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-13', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-14', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-15', '0 years 0 mons 0 days 7 hours 57 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-18', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-19', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-21', '0 years 0 mons 0 days 15 hours 18 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-22', '0 years 0 mons 0 days 4 hours 26 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-25', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-26', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-27', '0 years 0 mons 0 days 7 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-28', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-01', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-04', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-05', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-06', '0 years 0 mons 0 days 8 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-07', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-08', '0 years 0 mons 0 days 7 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-11', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-12', '0 years 0 mons 0 days 7 hours 34 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-13', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-14', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-15', '0 years 0 mons 0 days 8 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-18', '0 years 0 mons 0 days 7 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-19', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-20', '0 years 0 mons 0 days 8 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-21', '0 years 0 mons 0 days 8 hours 34 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-22', '0 years 0 mons 0 days 7 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-25', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-26', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-27', '0 years 0 mons 0 days 8 hours 28 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-28', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-29', '0 years 0 mons 0 days 6 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-01', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-02', '0 years 0 mons 0 days 8 hours 36 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-03', '0 years 0 mons 0 days 7 hours 57 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-04', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-05', '0 years 0 mons 0 days 8 hours 23 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-08', '0 years 0 mons 0 days 7 hours 52 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-09', '0 years 0 mons 0 days 8 hours 36 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-10', '0 years 0 mons 0 days 8 hours 36 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-11', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-12', '0 years 0 mons 0 days 7 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-15', '0 years 0 mons 0 days 10 hours 23 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-16', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-17', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-18', '0 years 0 mons 0 days 5 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-23', '0 years 0 mons 0 days 7 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-24', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-25', '0 years 0 mons 0 days 8 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-26', '0 years 0 mons 0 days 8 hours 12 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-06', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-07', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-08', '0 years 0 mons 0 days 10 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-09', '0 years 0 mons 0 days 7 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-10', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-13', '0 years 0 mons 0 days 8 hours 26 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-14', '0 years 0 mons 0 days 8 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-15', '0 years 0 mons 0 days 7 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-16', '0 years 0 mons 0 days 7 hours 39 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-17', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-20', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-21', '0 years 0 mons 0 days 9 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-22', '0 years 0 mons 0 days 8 hours 31 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-23', '0 years 0 mons 0 days 7 hours 41 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-24', '0 years 0 mons 0 days 7 hours 36 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-27', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-28', '0 years 0 mons 0 days 8 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-29', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-03', '0 years 0 mons 0 days 8 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-04', '0 years 0 mons 0 days 8 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-05', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-06', '0 years 0 mons 0 days 7 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-07', '0 years 0 mons 0 days 7 hours 35 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-11', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-12', '0 years 0 mons 0 days 8 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-13', '0 years 0 mons 0 days 8 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-14', '0 years 0 mons 0 days 4 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-17', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-18', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-19', '0 years 0 mons 0 days 8 hours 34 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-20', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-21', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-24', '0 years 0 mons 0 days 8 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-25', '0 years 0 mons 0 days 8 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-26', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-27', '0 years 0 mons 0 days 6 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-28', '0 years 0 mons 0 days 7 hours 18 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-01', '0 years 0 mons 0 days 7 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-02', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-03', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-04', '0 years 0 mons 0 days 7 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-05', '0 years 0 mons 0 days 8 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-08', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-09', '0 years 0 mons 0 days 8 hours 24 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-10', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-11', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-12', '0 years 0 mons 0 days 7 hours 15 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-15', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-16', '0 years 0 mons 0 days 8 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-17', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-18', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-19', '0 years 0 mons 0 days 7 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-22', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-23', '0 years 0 mons 0 days 8 hours 11 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-24', '0 years 0 mons 0 days 8 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-25', '0 years 0 mons 0 days 8 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-26', '0 years 0 mons 0 days 8 hours 17 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-29', '0 years 0 mons 0 days 8 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-30', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-31', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-01', '0 years 0 mons 0 days 9 hours 17 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-02', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-19', '0 years 0 mons 0 days 7 hours 39 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-20', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-21', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-22', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-23', '0 years 0 mons 0 days 7 hours 18 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-26', '0 years 0 mons 0 days 7 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-27', '0 years 0 mons 0 days 8 hours 15 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-28', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-29', '0 years 0 mons 0 days 7 hours 52 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-30', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-02', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-03', '0 years 0 mons 0 days 8 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-04', '0 years 0 mons 0 days 7 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-05', '0 years 0 mons 0 days 8 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-06', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-09', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-10', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-11', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-12', '0 years 0 mons 0 days 8 hours 26 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-13', '0 years 0 mons 0 days 8 hours 24 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-16', '0 years 0 mons 0 days 8 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-17', '0 years 0 mons 0 days 7 hours 48 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-18', '0 years 0 mons 0 days 6 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-19', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-20', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-23', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-24', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-25', '0 years 0 mons 0 days 7 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-26', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-27', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-30', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-01', '0 years 0 mons 0 days 8 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-02', '0 years 0 mons 0 days 7 hours 26 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-07', '0 years 0 mons 0 days 7 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-08', '0 years 0 mons 0 days 7 hours 52 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-09', '0 years 0 mons 0 days 8 hours 40 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-10', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-14', '0 years 0 mons 0 days 7 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-15', '0 years 0 mons 0 days 7 hours 45 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-21', '0 years 0 mons 0 days 7 hours 57 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-22', '0 years 0 mons 0 days 8 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-23', '0 years 0 mons 0 days 8 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-24', '0 years 0 mons 0 days 7 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-25', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-28', '0 years 0 mons 0 days 7 hours 27 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-29', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-30', '0 years 0 mons 0 days 7 hours 29 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-01', '0 years 0 mons 0 days 8 hours 48 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-04', '0 years 0 mons 0 days 7 hours 52 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-05', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-06', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-07', '0 years 0 mons 0 days 7 hours 24 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-08', '0 years 0 mons 0 days 8 hours 12 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-11', '0 years 0 mons 0 days 7 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-12', '0 years 0 mons 0 days 7 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-13', '0 years 0 mons 0 days 7 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-14', '0 years 0 mons 0 days 7 hours 40 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-15', '0 years 0 mons 0 days 7 hours 34 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-18', '0 years 0 mons 0 days 7 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-19', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-20', '0 years 0 mons 0 days 7 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-21', '0 years 0 mons 0 days 7 hours 39 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-22', '0 years 0 mons 0 days 7 hours 31 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-25', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-26', '0 years 0 mons 0 days 7 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-27', '0 years 0 mons 0 days 8 hours 29 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-28', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-29', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-02', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-03', '0 years 0 mons 0 days 7 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-04', '0 years 0 mons 0 days 8 hours 27 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-05', '0 years 0 mons 0 days 5 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-06', '0 years 0 mons 0 days 7 hours 35 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-09', '0 years 0 mons 0 days 7 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-10', '0 years 0 mons 0 days 8 hours 27 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-11', '0 years 0 mons 0 days 7 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-12', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-13', '0 years 0 mons 0 days 6 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-16', '0 years 0 mons 0 days 7 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-17', '0 years 0 mons 0 days 8 hours 33 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-18', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-19', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-20', '0 years 0 mons 0 days 6 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-07', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-08', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-09', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-10', '0 years 0 mons 0 days 7 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-13', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-14', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-15', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-16', '0 years 0 mons 0 days 7 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-17', '0 years 0 mons 0 days 7 hours 57 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-20', '0 years 0 mons 0 days 7 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-21', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-22', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-23', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-24', '0 years 0 mons 0 days 7 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-27', '0 years 0 mons 0 days 8 hours 23 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-28', '0 years 0 mons 0 days 7 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-29', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-30', '0 years 0 mons 0 days 8 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-31', '0 years 0 mons 0 days 7 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-03', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-04', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-05', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-06', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-07', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-10', '0 years 0 mons 0 days 7 hours 50 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-11', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-12', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-13', '0 years 0 mons 0 days 8 hours 12 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-14', '0 years 0 mons 0 days 7 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-17', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-18', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-19', '0 years 0 mons 0 days 7 hours 34 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-20', '0 years 0 mons 0 days 7 hours 33 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-21', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-24', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-25', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-26', '0 years 0 mons 0 days 8 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-27', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-28', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-02', '0 years 0 mons 0 days 8 hours 22 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-03', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-04', '0 years 0 mons 0 days 8 hours 12 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-05', '0 years 0 mons 0 days 8 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-06', '0 years 0 mons 0 days 4 hours 41 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-09', '0 years 0 mons 0 days 7 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-10', '0 years 0 mons 0 days 7 hours 34 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-11', '0 years 0 mons 0 days 7 hours 39 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-12', '0 years 0 mons 0 days 7 hours 50 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-13', '0 years 0 mons 0 days 8 hours 26 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-16', '0 years 0 mons 0 days 7 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-17', '0 years 0 mons 0 days 7 hours 50 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-18', '0 years 0 mons 0 days 7 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-19', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-20', '0 years 0 mons 0 days 5 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-23', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-24', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-25', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-26', '0 years 0 mons 0 days 10 hours 31 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-27', '0 years 0 mons 0 days 4 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-30', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-31', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-01', '0 years 0 mons 0 days 7 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-02', '0 years 0 mons 0 days 8 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-03', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-06', '0 years 0 mons 0 days 7 hours 27 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-07', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-08', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-14', '0 years 0 mons 0 days 7 hours 17 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-15', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-16', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-17', '0 years 0 mons 0 days 7 hours 39 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-20', '0 years 0 mons 0 days 8 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-21', '0 years 0 mons 0 days 8 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-22', '0 years 0 mons 0 days 8 hours 24 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-23', '0 years 0 mons 0 days 8 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-24', '0 years 0 mons 0 days 8 hours 24 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-27', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-28', '0 years 0 mons 0 days 8 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-29', '0 years 0 mons 0 days 8 hours 23 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-30', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-04', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-05', '0 years 0 mons 0 days 6 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-06', '0 years 0 mons 0 days 9 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-07', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-08', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-11', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-12', '0 years 0 mons 0 days 8 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-13', '0 years 0 mons 0 days 8 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-14', '0 years 0 mons 0 days 8 hours 39 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-15', '0 years 0 mons 0 days 6 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-18', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-19', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-20', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-25', '0 years 0 mons 0 days 7 hours 41 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-26', '0 years 0 mons 0 days 8 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-27', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-28', '0 years 0 mons 0 days 8 hours 24 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-26', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-29', '0 years 0 mons 0 days 8 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-02', '0 years 0 mons 0 days 7 hours 50 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-03', '0 years 0 mons 0 days 6 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-04', '0 years 0 mons 0 days 8 hours 27 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-05', '0 years 0 mons 0 days 7 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-08', '0 years 0 mons 0 days 7 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-09', '0 years 0 mons 0 days 7 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-10', '0 years 0 mons 0 days 7 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-11', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-15', '0 years 0 mons 0 days 7 hours 45 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-16', '0 years 0 mons 0 days 8 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-17', '0 years 0 mons 0 days 8 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-18', '0 years 0 mons 0 days 8 hours 23 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-19', '0 years 0 mons 0 days 8 hours 18 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-22', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-23', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-24', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-25', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-26', '0 years 0 mons 0 days 9 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-29', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-30', '0 years 0 mons 0 days 8 hours 18 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-01', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-02', '0 years 0 mons 0 days 7 hours 41 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-07', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-08', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-09', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-10', '0 years 0 mons 0 days 6 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-03', '0 years 0 mons 0 days 7 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-06', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-13', '0 years 0 mons 0 days 7 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-14', '0 years 0 mons 0 days 7 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-15', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-16', '0 years 0 mons 0 days 7 hours 57 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-17', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-10', '0 years 0 mons 0 days 7 hours 50 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-11', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-12', '0 years 0 mons 0 days 7 hours 12 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-13', '0 years 0 mons 0 days 7 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-14', '0 years 0 mons 0 days 8 hours 29 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-17', '0 years 0 mons 0 days 8 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-18', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-19', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-20', '0 years 0 mons 0 days 8 hours 17 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-21', '0 years 0 mons 0 days 7 hours 26 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-24', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-25', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-26', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-27', '0 years 0 mons 0 days 7 hours 31 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-28', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-31', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-01', '0 years 0 mons 0 days 8 hours 12 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-02', '0 years 0 mons 0 days 7 hours 45 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-03', '0 years 0 mons 0 days 8 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-04', '0 years 0 mons 0 days 7 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-07', '0 years 0 mons 0 days 7 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-08', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-09', '0 years 0 mons 0 days 8 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-10', '0 years 0 mons 0 days 6 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-11', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-14', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-15', '0 years 0 mons 0 days 7 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-16', '0 years 0 mons 0 days 7 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-17', '0 years 0 mons 0 days 7 hours 33 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-18', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-21', '0 years 0 mons 0 days 7 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-22', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-23', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-24', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-25', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-28', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-29', '0 years 0 mons 0 days 7 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-30', '0 years 0 mons 0 days 8 hours 15 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-01', '0 years 0 mons 0 days 7 hours 27 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-02', '0 years 0 mons 0 days 7 hours 22 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-05', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-06', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-07', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-08', '0 years 0 mons 0 days 7 hours 29 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-09', '0 years 0 mons 0 days 9 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-12', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-13', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-14', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-15', '0 years 0 mons 0 days 7 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-16', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-19', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-20', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-21', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-22', '0 years 0 mons 0 days 7 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-23', '0 years 0 mons 0 days 7 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-27', '0 years 0 mons 0 days 9 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-28', '0 years 0 mons 0 days 7 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-29', '0 years 0 mons 0 days 7 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-30', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-02', '0 years 0 mons 0 days 8 hours 11 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-04', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-05', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-06', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-09', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-11', '0 years 0 mons 0 days 8 hours 28 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-12', '0 years 0 mons 0 days 7 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-13', '0 years 0 mons 0 days 9 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-16', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-17', '0 years 0 mons 0 days 8 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-18', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-19', '0 years 0 mons 0 days 8 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-20', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-03', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-10', '0 years 0 mons 0 days 8 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-23', '0 years 0 mons 0 days 8 hours 17 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-24', '0 years 0 mons 0 days 7 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-25', '0 years 0 mons 0 days 7 hours 52 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-26', '0 years 0 mons 0 days 7 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-27', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-30', '0 years 0 mons 0 days 8 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-01', '0 years 0 mons 0 days 8 hours 33 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-02', '0 years 0 mons 0 days 6 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-03', '0 years 0 mons 0 days 7 hours 48 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-04', '0 years 0 mons 0 days 8 hours 29 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-07', '0 years 0 mons 0 days 8 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-09', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-10', '0 years 0 mons 0 days 7 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-11', '0 years 0 mons 0 days 7 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-08', '0 years 0 mons 0 days 8 hours 22 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-14', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-15', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-16', '0 years 0 mons 0 days 7 hours 36 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-17', '0 years 0 mons 0 days 7 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-18', '0 years 0 mons 0 days 7 hours 33 mins 0.00 secs');
|
||||||
Executable
+62
@@ -0,0 +1,62 @@
|
|||||||
|
Aktivität;Dauer;Von;Bis;Kommentar
|
||||||
|
Arbeit;04:19;2021-07-30 13:01;2021-07-30 17:20;
|
||||||
|
Mittagspause ;00:45;2021-07-30 12:16;2021-07-30 13:01;
|
||||||
|
Arbeit;04:22;2021-07-30 07:54;2021-07-30 12:16;
|
||||||
|
Arbeit;03:25;2021-07-29 13:25;2021-07-29 16:50;
|
||||||
|
Mittagspause ;01:01;2021-07-29 12:23;2021-07-29 13:25;
|
||||||
|
Arbeit;04:44;2021-07-29 07:39;2021-07-29 12:23;
|
||||||
|
Arbeit;04:47;2021-07-28 12:49;2021-07-28 17:37;
|
||||||
|
Mittagspause ;00:58;2021-07-28 11:51;2021-07-28 12:49;
|
||||||
|
Arbeit;03:48;2021-07-28 08:02;2021-07-28 11:51;
|
||||||
|
Arbeit;03:49;2021-07-27 12:34;2021-07-27 16:24;
|
||||||
|
Mittagspause ;00:47;2021-07-27 11:47;2021-07-27 12:34;
|
||||||
|
Arbeit;03:20;2021-07-27 08:27;2021-07-27 11:47;
|
||||||
|
Arbeit;04:47;2021-07-26 12:59;2021-07-26 17:47;
|
||||||
|
Mittagspause ;00:52;2021-07-26 12:07;2021-07-26 12:59;
|
||||||
|
Arbeit;03:59;2021-07-26 08:08;2021-07-26 12:07;
|
||||||
|
Arbeit;05:53;2021-07-23 12:43;2021-07-23 18:37;
|
||||||
|
Mittagspause ;00:36;2021-07-23 12:07;2021-07-23 12:43;
|
||||||
|
Arbeit;04:10;2021-07-23 07:57;2021-07-23 12:07;
|
||||||
|
Arbeit;04:09;2021-07-22 12:54;2021-07-22 17:04;
|
||||||
|
Mittagspause ;00:42;2021-07-22 12:11;2021-07-22 12:54;
|
||||||
|
Arbeit;04:16;2021-07-22 07:55;2021-07-22 12:11;
|
||||||
|
Arbeit;03:11;2021-07-21 13:37;2021-07-21 16:48;
|
||||||
|
Mittagspause ;01:07;2021-07-21 12:29;2021-07-21 13:37;
|
||||||
|
Arbeit;04:58;2021-07-21 07:31;2021-07-21 12:29;
|
||||||
|
Arbeit;01:17;2021-07-20 18:44;2021-07-20 20:02;
|
||||||
|
Arbeit;03:50;2021-07-20 13:14;2021-07-20 17:04;
|
||||||
|
Mittagspause ;00:41;2021-07-20 12:32;2021-07-20 13:14;
|
||||||
|
Arbeit;04:55;2021-07-20 07:37;2021-07-20 12:32;
|
||||||
|
Arbeit;03:19;2021-07-19 13:12;2021-07-19 16:31;
|
||||||
|
Mittagspause ;00:52;2021-07-19 12:20;2021-07-19 13:12;
|
||||||
|
Arbeit;04:28;2021-07-19 07:51;2021-07-19 12:20;
|
||||||
|
Arbeit;02:48;2021-07-16 12:55;2021-07-16 15:43;
|
||||||
|
Mittagspause ;00:47;2021-07-16 12:07;2021-07-16 12:55;
|
||||||
|
Arbeit;04:01;2021-07-16 08:06;2021-07-16 12:07;
|
||||||
|
Arbeit;03:38;2021-07-15 13:05;2021-07-15 16:43;
|
||||||
|
Mittagspause ;01:05;2021-07-15 11:59;2021-07-15 13:05;
|
||||||
|
Arbeit;03:56;2021-07-15 08:02;2021-07-15 11:59;
|
||||||
|
Arbeit;03:38;2021-07-14 13:07;2021-07-14 16:45;
|
||||||
|
Mittagspause ;01:00;2021-07-14 12:07;2021-07-14 13:07;
|
||||||
|
Arbeit;04:00;2021-07-14 08:06;2021-07-14 12:07;
|
||||||
|
Arbeit;04:22;2021-07-13 12:53;2021-07-13 17:15;
|
||||||
|
Mittagspause ;01:03;2021-07-13 11:50;2021-07-13 12:53;
|
||||||
|
Arbeit;03:47;2021-07-13 08:02;2021-07-13 11:50;
|
||||||
|
Arbeit;03:26;2021-07-12 13:02;2021-07-12 16:28;
|
||||||
|
Mittagspause ;01:03;2021-07-12 11:58;2021-07-12 13:02;
|
||||||
|
Arbeit;04:14;2021-07-12 07:44;2021-07-12 11:58;
|
||||||
|
Arbeit;03:57;2021-07-09 13:09;2021-07-09 17:06;
|
||||||
|
Mittagspause ;01:06;2021-07-09 12:02;2021-07-09 13:09;
|
||||||
|
Arbeit;03:57;2021-07-09 08:04;2021-07-09 12:02;
|
||||||
|
Arbeit;03:17;2021-07-08 13:12;2021-07-08 16:29;
|
||||||
|
Mittagspause ;01:07;2021-07-08 12:04;2021-07-08 13:12;
|
||||||
|
Arbeit;04:00;2021-07-08 08:03;2021-07-08 12:04;
|
||||||
|
Arbeit;03:41;2021-07-07 13:08;2021-07-07 16:49;
|
||||||
|
Mittagspause ;01:00;2021-07-07 12:07;2021-07-07 13:08;
|
||||||
|
Arbeit;04:05;2021-07-07 08:02;2021-07-07 12:07;
|
||||||
|
Arbeit;02:50;2021-07-06 13:05;2021-07-06 15:55;
|
||||||
|
Mittagspause ;01:05;2021-07-06 11:59;2021-07-06 13:05;
|
||||||
|
Arbeit;03:49;2021-07-06 08:10;2021-07-06 11:59;
|
||||||
|
Arbeit;03:32;2021-07-05 13:00;2021-07-05 16:33;
|
||||||
|
Mittagspause ;00:59;2021-07-05 12:00;2021-07-05 13:00;
|
||||||
|
Arbeit;03:48;2021-07-05 08:12;2021-07-05 12:00;
|
||||||
|
@@ -0,0 +1,15 @@
|
|||||||
|
@startuml
|
||||||
|
'https://plantuml.com/activity-diagram-beta
|
||||||
|
|UI|
|
||||||
|
:Bestimme //Startdatum//;
|
||||||
|
:Bestimme //Enddatum//;
|
||||||
|
:Erstelle //Liste der Wochentage//;
|
||||||
|
|REST|
|
||||||
|
start
|
||||||
|
:Rufe API auf;
|
||||||
|
|UI|
|
||||||
|
:Zeige //Liste der Wochentage// an;
|
||||||
|
:Verarbeite Informationen der Rest-API;
|
||||||
|
stop
|
||||||
|
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Url ohne abschlißenden Slash
|
||||||
|
API_URL= http://localhost:8080
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
public
|
||||||
|
/.env
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
FROM node:22-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
RUN npm prune --production
|
||||||
|
|
||||||
|
FROM node:22-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=builder /app/build build/
|
||||||
|
COPY --from=builder /app/node_modules node_modules/
|
||||||
|
COPY package.json .
|
||||||
|
EXPOSE 3000
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
CMD [ "node", "build" ]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// Einstellungen auf die Grundlagen zurücksetzen,
|
||||||
|
// beinhaltet auch Einstellungen für die neuen HTML5-Tags
|
||||||
|
//noinspection CssUnknownTarget
|
||||||
|
@import "meyer_resets.css";
|
||||||
|
// Import und Einbindung der Zeichensätze
|
||||||
|
@import "lesslib/fonts.less";
|
||||||
|
// Bestimmung der Farben
|
||||||
|
@import "lesslib/colors.less";
|
||||||
|
// Mixins des Layouts
|
||||||
|
@import "lesslib/mixins.less";
|
||||||
|
// Layout-Unabhängige Mixins
|
||||||
|
@import "lesslib/mixins.framework.less";
|
||||||
|
// Bestimmung des grundsätzlichen Erscheinungsbildes der einzelnen Elemente,
|
||||||
|
// einschließlich des Hauptbereiches
|
||||||
|
@import "lesslib/basics.less";
|
||||||
|
// Darstellung unabhängig der Darstellungsgrößen
|
||||||
|
@import "lesslib/layout.less";
|
||||||
|
// Darstellung in den verschiedenen Ansichtsgrößen
|
||||||
|
@import "lesslib/media_queries.less";
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
html {
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: #000;
|
||||||
|
font-size: 1em;
|
||||||
|
font-family: OpenSans, "Lucida Grande", "Lucida Sans Unicode", Verdana, Helvetica, Arial, sans-serif;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong, b {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
em, i {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
section article {
|
||||||
|
font-size: 95%;
|
||||||
|
p, ul, ol, dl {
|
||||||
|
line-height: 1.5em;
|
||||||
|
.hyphens();
|
||||||
|
font-size: 1.00em;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
ul, ol {
|
||||||
|
li {
|
||||||
|
list-style: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dl {
|
||||||
|
dt {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h1, h2, h3 {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 1.50em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.30em;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.20em;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
colgroup:not(:first-child) col:first-child{
|
||||||
|
border-left: solid thin;
|
||||||
|
}
|
||||||
|
thead {
|
||||||
|
th {
|
||||||
|
font-size: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
fieldset {
|
||||||
|
legend {
|
||||||
|
font-size: 1.30em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Einstellungen für die Pagination-Navigation
|
||||||
|
.pagination-nav {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 1rem;
|
||||||
|
.option {
|
||||||
|
&.number, &.ellipsis {
|
||||||
|
line-height: 1.25rem;
|
||||||
|
span {
|
||||||
|
margin: .25rem .5rem;
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
text-decoration: underline solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.disabled {
|
||||||
|
svg {
|
||||||
|
path {
|
||||||
|
fill: darken(@mainColorLight,40%,relativ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
Definition der Farben
|
||||||
|
*/
|
||||||
|
|
||||||
|
@hueMain: 35;
|
||||||
|
@hueSub: 215;
|
||||||
|
|
||||||
|
@mainColor: hsl(@hueMain, 90%, 29%);
|
||||||
|
@redColor: hsl(0, 90%, 29%);
|
||||||
|
@mainColorDark: darken(@mainColor,66%,relativ);
|
||||||
|
@mainColorLight: lighten(@mainColor,66%,relativ);
|
||||||
|
@subColor: hsl(@hueSub, 90%, 29%);
|
||||||
|
@subColorDark: darken(@subColor,66%,relativ);
|
||||||
|
@subColorLight: lighten(@subColor,66%,relativ);
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: OpenSans;
|
||||||
|
src: url(../fonts/opensans-regular.eot);
|
||||||
|
src: url(../fonts/opensans-regular.eot?#iefix) format("embedded-opentype"),
|
||||||
|
url(../fonts/opensans-regular.woff) format("woff"),
|
||||||
|
url(../fonts/opensans-regular.ttf) format("truetype"),
|
||||||
|
url(../fonts/opensans-regular.svg#open_sansregular) format("svg");
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: OpenSans;
|
||||||
|
src: url(../fonts/opensans-italic.eot);
|
||||||
|
src: url(../fonts/opensans-italic.eot?#iefix) format("embedded-opentype"),
|
||||||
|
url(../fonts/opensans-italic.woff) format("woff"),
|
||||||
|
url(../fonts/opensans-italic.ttf) format("truetype"),
|
||||||
|
url(../fonts/opensans-italic.svg#open_sansitalic) format("svg");
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: OpenSans;
|
||||||
|
src: url(../fonts/opensans-bold.eot);
|
||||||
|
src: url(../fonts/opensans-bold.eot?#iefix) format("embedded-opentype"),
|
||||||
|
url(../fonts/opensans-bold.woff) format("woff"),
|
||||||
|
url(../fonts/opensans-bold.ttf) format("truetype"),
|
||||||
|
url(../fonts/opensans-bold.svg#open_sansbold) format("svg");
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: OpenSans;
|
||||||
|
src: url(../fonts/opensans-bolditalic.eot);
|
||||||
|
src: url(../fonts/opensans-bolditalic.eot?#iefix) format("embedded-opentype"),
|
||||||
|
url(../fonts/opensans-bolditalic.woff) format("woff"),
|
||||||
|
url(../fonts/opensans-bolditalic.ttf) format("truetype"),
|
||||||
|
url(../fonts/opensans-bolditalic.svg#open_sansbold_italic) format("svg");
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
@breiteMainBereich: 70rem;
|
||||||
|
@breiteTextBereich: 60rem;
|
||||||
|
@breiteNavBereich: @breiteMainBereich - @breiteTextBereich;
|
||||||
|
|
||||||
|
html {
|
||||||
|
body {
|
||||||
|
background: @mainColorLight;
|
||||||
|
div {
|
||||||
|
> header, > footer {
|
||||||
|
color: @mainColorDark;
|
||||||
|
background: @mainColor;
|
||||||
|
font-family: serif;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
text-indent: inherit;
|
||||||
|
|
||||||
|
&:first-letter {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> header h1 {
|
||||||
|
margin: 1rem auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
> footer {
|
||||||
|
color: contrast(@mainColorDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > nav {
|
||||||
|
ul {
|
||||||
|
background: darken(@mainColorLight, 33%, relativ);
|
||||||
|
|
||||||
|
li {
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
border-left: @mainColorDark solid thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: @mainColorDark;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: lighten(@mainColor, 20%, relativ);
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: lighten(@mainColor, 66%, relativ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected, &.selected:hover {
|
||||||
|
background: lighten(@mainColor, 10%, relativ);
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: lighten(@mainColor, 66%, relativ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
* {
|
||||||
|
border-style: none;
|
||||||
|
border-color: @mainColorDark;
|
||||||
|
border-width: medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
@sectionMargin: 1rem;
|
||||||
|
|
||||||
|
h1, h2 {
|
||||||
|
margin: @sectionMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, ul, ol, dl {
|
||||||
|
margin: @sectionMargin;
|
||||||
|
text-indent: @sectionMargin * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
thead {
|
||||||
|
tr:last-child {
|
||||||
|
td, th {
|
||||||
|
border-bottom: @mainColor solid thin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
td.absence-time {
|
||||||
|
color: @redColor;
|
||||||
|
text-decoration-line: underline;
|
||||||
|
text-decoration-style: dashed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tfoot {
|
||||||
|
* {
|
||||||
|
border: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
@formLabelWidth: 10rem;
|
||||||
|
@formItemWidth: 40rem;
|
||||||
|
|
||||||
|
nav.menu {
|
||||||
|
width: @formItemWidth + @formLabelWidth + 4rem;
|
||||||
|
border-color: @mainColor;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
margin: 1rem auto;
|
||||||
|
|
||||||
|
span {
|
||||||
|
border-color: @mainColor;
|
||||||
|
border-style: solid solid none;
|
||||||
|
padding: 0.5rem;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bolder;
|
||||||
|
margin-right: 1rem;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: darken(@mainColorLight, 10%, relativ);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: darken(@mainColorLight, 20%, relativ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
width: @formItemWidth + @formLabelWidth + 2rem;
|
||||||
|
margin: 1rem auto;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: @mainColor;
|
||||||
|
padding: @sectionMargin;
|
||||||
|
|
||||||
|
&.tasten {
|
||||||
|
border: none;
|
||||||
|
background-color: @mainColorLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
margin: @sectionMargin;
|
||||||
|
margin-left: @sectionMargin * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
& > label {
|
||||||
|
width: @formLabelWidth;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
display: inline-block;
|
||||||
|
width: @formItemWidth;
|
||||||
|
padding: 0.25rem;
|
||||||
|
margin-bottom: 1.0rem;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: thin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
.button(@mainColor, spin(lighten(@mainColor, 66%, relativ), @subColor));
|
||||||
|
|
||||||
|
&.sub-button {
|
||||||
|
.button(lighten(@mainColor, 20%, relativ), spin(@mainColor, @subColor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
text-align: right;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
p {
|
||||||
|
padding: 0.5rem;
|
||||||
|
display: inline-block;
|
||||||
|
margin: inherit;
|
||||||
|
text-indent: inherit;
|
||||||
|
|
||||||
|
&:first-letter {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
background: @mainColorLight;
|
||||||
|
color: @mainColor;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav li {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,306 @@
|
|||||||
|
//Media Queries
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
* {
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
header, footer, main, nav, aside {
|
||||||
|
body div > & {
|
||||||
|
width: 19cm;
|
||||||
|
margin: auto;
|
||||||
|
.border-box();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header, footer{
|
||||||
|
padding: 0.5rem;
|
||||||
|
;
|
||||||
|
}
|
||||||
|
body div {
|
||||||
|
& > header {
|
||||||
|
font-size: 200%;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
& > nav, > header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
padding: 1.5rem 0.5rem 3.25rem;
|
||||||
|
.flexKinder(flow,'row');
|
||||||
|
width: @breiteTextBereich;
|
||||||
|
section {
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
h1, h2, p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
padding: 0;
|
||||||
|
p {
|
||||||
|
padding: 0.5rem;
|
||||||
|
font-size: 85%;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
article.liste {
|
||||||
|
page-break-after: always;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h1, h2 {
|
||||||
|
margin-left: 5em;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.75em;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
margin: auto;
|
||||||
|
td, th {
|
||||||
|
padding: 0.5rem;
|
||||||
|
collapse: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aside {
|
||||||
|
padding-top: 2rem;
|
||||||
|
.flexKinder(flow,'row');
|
||||||
|
width: @breiteNavBereich;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 831px) {
|
||||||
|
header, footer, main, nav, aside {
|
||||||
|
body div > & {
|
||||||
|
width: @breiteMainBereich;
|
||||||
|
margin: auto;
|
||||||
|
.border-box();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header, footer{
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
body div {
|
||||||
|
& > header {
|
||||||
|
font-size: 200%;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
& > nav {
|
||||||
|
ul {
|
||||||
|
.flexEltern();
|
||||||
|
margin: 0;
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
height: 1.5rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
padding: 1.5rem 0.5rem 3.25rem;
|
||||||
|
.flexKinder(flow,'row');
|
||||||
|
width: @breiteTextBereich;
|
||||||
|
section {
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
h1, h2, p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
padding: 0;
|
||||||
|
p {
|
||||||
|
padding: 0.5rem;
|
||||||
|
font-size: 85%;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h1, h2 {
|
||||||
|
margin-left: 5em;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.75em;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
margin: auto;
|
||||||
|
td, th {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aside {
|
||||||
|
padding-top: 2rem;
|
||||||
|
.flexKinder(flow,'row');
|
||||||
|
width: @breiteNavBereich;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 830px) {
|
||||||
|
html body div {
|
||||||
|
@boxHeight: 5rem;
|
||||||
|
& > header {
|
||||||
|
height: @boxHeight;
|
||||||
|
font-size: @boxHeight;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
& > footer {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
& > nav {
|
||||||
|
ul {
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
border: none;
|
||||||
|
width: auto;
|
||||||
|
height: 1.5em;
|
||||||
|
padding-top: 0.25em;
|
||||||
|
a {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
&:not(:first-child) {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main section {
|
||||||
|
header {
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
h1, h2, p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
h1, h2 {
|
||||||
|
margin-left: 5em;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.75em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
@formLabelWidth: 10rem;
|
||||||
|
@formItemWidth: 40rem;
|
||||||
|
@sectionMargin: 1rem;
|
||||||
|
margin: @sectionMargin;
|
||||||
|
fieldset {
|
||||||
|
width: auto;
|
||||||
|
margin: 1rem auto;
|
||||||
|
padding: @sectionMargin;
|
||||||
|
legend {
|
||||||
|
margin: @sectionMargin;
|
||||||
|
margin-left: @sectionMargin * 2;
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
& > label {
|
||||||
|
width: auto;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
input, textarea {
|
||||||
|
display: inline-block;
|
||||||
|
width: auto;
|
||||||
|
padding: 0.25rem;
|
||||||
|
margin: 0.5rem;
|
||||||
|
margin-bottom: 1.0rem;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: thin;
|
||||||
|
}
|
||||||
|
div.radio-button {
|
||||||
|
padding-left: 20%;
|
||||||
|
label, input {
|
||||||
|
display: inline;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
margin-top: inherit;
|
||||||
|
margin-right: 1rem;
|
||||||
|
margin-left: 1rem;
|
||||||
|
margin-bottom: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
height: 10rem;
|
||||||
|
}
|
||||||
|
ul.suche {
|
||||||
|
width: 30rem;
|
||||||
|
margin-left: @formLabelWidth + 1.5rem;
|
||||||
|
margin-top: -1rem;
|
||||||
|
background: white;
|
||||||
|
position: absolute;
|
||||||
|
border: solid thin;
|
||||||
|
li {list-style: none;
|
||||||
|
text-indent: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background: @mainColorLight;
|
||||||
|
color: @mainColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 90%;
|
||||||
|
margin: 5%;
|
||||||
|
td, th {
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
colgroup:not(:first-child) col:first-child{
|
||||||
|
border-left: solid thin;
|
||||||
|
}
|
||||||
|
thead {
|
||||||
|
th {
|
||||||
|
font-size: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 720px) {
|
||||||
|
html body div {
|
||||||
|
@boxHeight: 2.5rem;
|
||||||
|
& > header {
|
||||||
|
height: @boxHeight;
|
||||||
|
font-size: @boxHeight;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > nav {
|
||||||
|
.hamburgerMenu();
|
||||||
|
}
|
||||||
|
main section header {
|
||||||
|
h1, h2 {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0.5rem auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
margin: 1rem 0;
|
||||||
|
td, th {
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
colgroup:not(:first-child) col:first-child{
|
||||||
|
border-left: solid thin;
|
||||||
|
}
|
||||||
|
thead {
|
||||||
|
th {
|
||||||
|
font-size: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
.hyphens();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
.hyphens() {
|
||||||
|
-webkit-hyphens: auto;
|
||||||
|
-moz-hyphens: auto;
|
||||||
|
hyphens: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-box() {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-gradient(@stop1, @stop2) {
|
||||||
|
.gradient(0deg, @stop1, @stop2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-gradient(@stop1, @stop2) {
|
||||||
|
.gradient(-90deg, @stop1, @stop2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient(@winkel, @stop1, @stop2) {
|
||||||
|
@winkelOld: @winkel + 90deg;
|
||||||
|
@groundColor: @stop2;
|
||||||
|
background: @groundColor -moz-linear-gradient(@winkelOld, @stop2, @stop1);
|
||||||
|
background: @groundColor -webkit-linear-gradient(@winkelOld, @stop2, @stop1);
|
||||||
|
background: @groundColor -o-linear-gradient(@winkelOld, @stop2, @stop1);
|
||||||
|
background: @groundColor -ms-linear-gradient(@winkelOld, @stop2, @stop1);
|
||||||
|
background: @groundColor linear-gradient(@winkel, @stop2, @stop1);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-shadow(...) {
|
||||||
|
-webkit-box-shadow: @arguments;
|
||||||
|
-moz-box-shadow: @arguments;
|
||||||
|
box-shadow: @arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-radius(...) {
|
||||||
|
-webkit-border-radius: @arguments;
|
||||||
|
-moz-border-radius: @arguments;
|
||||||
|
border-radius: @arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nowrap() {
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburgerMenu() {
|
||||||
|
color: white;
|
||||||
|
width: 3rem;
|
||||||
|
height: 2rem;
|
||||||
|
top: 0.25rem;
|
||||||
|
left: 0.25rem;
|
||||||
|
.border-radius(4px);
|
||||||
|
border: none;
|
||||||
|
.vertical-gradient(@mainColorDark; @mainColor);
|
||||||
|
> ul {
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 100%;
|
||||||
|
background: fade(@mainColorDark,80%);
|
||||||
|
.border-radius(5px);
|
||||||
|
display: none;
|
||||||
|
> li {
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
line-height: normal;
|
||||||
|
border: none;
|
||||||
|
&:not(:first-child) {
|
||||||
|
border-top: 1px #888 solid;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
.nowrap();
|
||||||
|
display: block;
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
color: white;
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: white;
|
||||||
|
.vertical-gradient(@mainColorLight; @mainColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:first-child {
|
||||||
|
a {
|
||||||
|
.border-radius( 5px 5px 0 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
a {
|
||||||
|
.border-radius( 0 0 5px 5px );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover, &:focus, &:active, &:checked {
|
||||||
|
ul {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:before {
|
||||||
|
content: "\2261";
|
||||||
|
font-size: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 0.8em;
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexEltern() {
|
||||||
|
display:-ms-flexbox;
|
||||||
|
display: flexbox;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexEltern(column) {
|
||||||
|
display:-ms-flexbox;
|
||||||
|
display: flexbox;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: flex;
|
||||||
|
-webkit-flex-direction: column;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexKinder(flow, @rest...) {
|
||||||
|
-webkit-flex-flow: @rest;
|
||||||
|
-ms-flex-flow: @rest;
|
||||||
|
flex-flow: @rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexKinder(glow, @num) {
|
||||||
|
-webkit-flex-grow: @num;
|
||||||
|
-ms-flex: @num;
|
||||||
|
flex: @num;
|
||||||
|
flex-grow: @num;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
.button(@color, @colorFont) {
|
||||||
|
@colorBackground: @color;
|
||||||
|
@colorBackgroundLight: lighten(@colorBackground,50%,relativ);
|
||||||
|
@colorShadow: fade(@colorBackground,50%);
|
||||||
|
height: 2.5em;
|
||||||
|
.border-radius(1.25em);
|
||||||
|
padding: 0.5em 1.25em;
|
||||||
|
.vertical-gradient(@colorBackgroundLight, @colorBackground);
|
||||||
|
.box-shadow(0.1em 0.2em 0.5em @colorShadow);
|
||||||
|
color: @colorFont;
|
||||||
|
font-weight: bold;
|
||||||
|
&:hover {
|
||||||
|
.vertical-gradient(@colorBackground, @colorBackgroundLight);
|
||||||
|
}
|
||||||
|
margin: 0.25rem;
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
html {
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body div {
|
||||||
|
color: #000;
|
||||||
|
font-size: 1em;
|
||||||
|
font-family: OpenSans, "Lucida Grande", "Lucida Sans Unicode", Verdana, Helvetica, Arial, sans-serif;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong, b {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
em, i {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
section article {
|
||||||
|
font-size: 95%;
|
||||||
|
p, ul, ol, dl {
|
||||||
|
line-height: 1.5em;
|
||||||
|
//noinspection TaskProblemsInspection,LessResolvedByNameOnly
|
||||||
|
.hyphens();
|
||||||
|
font-size: 1.00em;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
ul, ol {
|
||||||
|
li {
|
||||||
|
list-style: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dl {
|
||||||
|
dt {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h1, h2, h3 {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 1.50em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.30em;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.20em;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
thead {
|
||||||
|
th {
|
||||||
|
font-size: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
fieldset {
|
||||||
|
legend {
|
||||||
|
font-size: 1.30em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
div.zelle > label {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.http-error {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
Definition der Farben
|
||||||
|
*/
|
||||||
|
|
||||||
|
@hueMain: 180;
|
||||||
|
@hueSub: 60;
|
||||||
|
|
||||||
|
@mainColor: hsl(@hueMain, 90%, 29%);
|
||||||
|
@mainColorDark: darken(@mainColor,66%,relativ);
|
||||||
|
@mainColorLight: lighten(@mainColor,66%,relativ);
|
||||||
|
@subColor: hsl(@hueSub, 90%, 29%);
|
||||||
|
@subColorDark: darken(@subColor,66%,relativ);
|
||||||
|
@subColorLight: lighten(@subColor,66%,relativ);
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
html {
|
||||||
|
body div {
|
||||||
|
> header, > footer {
|
||||||
|
background: none;
|
||||||
|
font-family: serif;
|
||||||
|
h1 {
|
||||||
|
font-size: 3rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
text-indent: inherit;
|
||||||
|
&:first-letter {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> header {
|
||||||
|
border-bottom: black solid thick;
|
||||||
|
h1 {
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> nav {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
> footer {
|
||||||
|
color: contrast(@mainColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
* {
|
||||||
|
border-style: none;
|
||||||
|
border-color: @mainColorDark;
|
||||||
|
border-width: medium;
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
@sectionMargin: 1rem;
|
||||||
|
header {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
h1, h2 {
|
||||||
|
margin: @sectionMargin;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: @sectionMargin;
|
||||||
|
text-indent: @sectionMargin * 2;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
colgroup:not(:first-child) col:first-child{
|
||||||
|
border-left: solid medium;
|
||||||
|
}
|
||||||
|
thead {
|
||||||
|
tr:last-child {
|
||||||
|
td, th {
|
||||||
|
border-bottom: @mainColorDark solid thick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tbody div {
|
||||||
|
th {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem 0.5rem;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-bottom: solid thin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
text-align: right;
|
||||||
|
padding: 0;
|
||||||
|
p {
|
||||||
|
padding: 0.5rem;
|
||||||
|
display: inline-block;
|
||||||
|
margin: inherit;
|
||||||
|
text-indent: inherit;
|
||||||
|
&:first-letter {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
background: @mainColorLight ;
|
||||||
|
color: @mainColor ;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 85%;
|
||||||
|
}
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// Einstellungen auf die Grundlagen zurücksetzen,
|
||||||
|
// beinhaltet auch Einstellungen für die neuen HTML5-Tags
|
||||||
|
//noinspection CssUnknownTarget
|
||||||
|
@import "meyer_resets.css";
|
||||||
|
// Bestimmung der Farben
|
||||||
|
@import "lesslib/colors.less";
|
||||||
|
// Mixins des Layouts
|
||||||
|
@import "lesslib/mixins.less";
|
||||||
|
// Layout-Unabhängige Mixins
|
||||||
|
@import "lesslib/mixins.framework.less";
|
||||||
|
// Bestimmung des grundsätzlichen Erscheinungsbildes der einzelnen Elemente,
|
||||||
|
// einschließlich des Hauptbereiches
|
||||||
|
@import "lesslib/print/basics.less";
|
||||||
|
// Darstellung unabhängig der Darstellungsgrößen
|
||||||
|
@import "lesslib/print/layout.less";
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "svelte-demo",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite dev",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"prepare": "svelte-kit sync || echo ''",
|
||||||
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ganalytics": "^3.1.2",
|
||||||
|
"navaid": "^1.0.2",
|
||||||
|
"open-iconic": "^1.1.1",
|
||||||
|
"papaparse": "^5.5.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@sveltejs/adapter-auto": "^6.0.0",
|
||||||
|
"@sveltejs/kit": "^2.16.0",
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||||
|
"@sveltejs/adapter-node": "^5.2.12",
|
||||||
|
"svelte": "^5.0.0",
|
||||||
|
"svelte-check": "^4.0.0",
|
||||||
|
"typescript": "^5.0.0",
|
||||||
|
"vite": "^6.2.6",
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"wx-svelte-grid": "^2.1.5",
|
||||||
|
"@iconify-icons/oi": "^1.1.0",
|
||||||
|
"@iconify/svelte": "^5.0.0"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"svelte": "./puplic/index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<base href="/"/>
|
||||||
|
<meta charset="utf8">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
|
||||||
|
<title>Svelte app</title>
|
||||||
|
|
||||||
|
<link rel='icon' type='image/png' href='%sveltekit.assets%/favicon.png'>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<meta name="color-scheme" content="dark light"/>
|
||||||
|
%sveltekit.head%
|
||||||
|
<link rel="stylesheet" href="%sveltekit.assets%/global.css" media="screen">
|
||||||
|
<link rel="stylesheet" href="%sveltekit.assets%/print.css" media="print">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div style="display: contents">
|
||||||
|
%sveltekit.body%
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export class PeriodRecord {
|
||||||
|
/**
|
||||||
|
* @param {{ workingDays: Number; totalHours: any; overtime: string; period: string; }} record
|
||||||
|
*/
|
||||||
|
constructor(record) {
|
||||||
|
this.workingDays = Number(record.workingDays)
|
||||||
|
this.totalHours = String(record.totalHours)
|
||||||
|
this.period = String(record.period)
|
||||||
|
this.overtime = String('##:##:##')
|
||||||
|
this.isMinus = false
|
||||||
|
const interval = record.overtime.match(/^(-?)(\d\d:\d\d:\d\d)$/)
|
||||||
|
if (interval) {
|
||||||
|
this.overtime = String(interval[2])
|
||||||
|
this.isMinus = String(interval[1]) === '-'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
<script module>
|
||||||
|
|
||||||
|
const MyFetch = {
|
||||||
|
/**
|
||||||
|
* @param {String} rest_url
|
||||||
|
* @param {String} request_body
|
||||||
|
*/
|
||||||
|
'post': async (rest_url, request_body) => {
|
||||||
|
return MyFetch._fetch(rest_url, MyFetch._buildRequestOptions(request_body, false))
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} rest_url
|
||||||
|
* @param {String} http_body
|
||||||
|
*/
|
||||||
|
'put': async (rest_url, http_body) => {
|
||||||
|
return MyFetch._fetch(rest_url, MyFetch._buildRequestOptions(http_body, true))
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} rest_url
|
||||||
|
* @param {Object|null} query_parameters
|
||||||
|
*/
|
||||||
|
'get': async (rest_url, query_parameters = null) => {
|
||||||
|
if (query_parameters !== null) {
|
||||||
|
let filter = []
|
||||||
|
for (let parameter in query_parameters) {
|
||||||
|
filter.push(parameter + '=' + query_parameters[parameter])
|
||||||
|
}
|
||||||
|
if (filter.length > 0) {
|
||||||
|
rest_url += '?' + filter.join('&')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MyFetch._fetch(rest_url, {})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} rest_url
|
||||||
|
* @param {Object} request_options
|
||||||
|
*/
|
||||||
|
'_fetch': async (rest_url, request_options) => {
|
||||||
|
// noinspection JSUnresolvedVariable
|
||||||
|
let response = await MyFetch._pureFetch(rest_url, request_options).catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
if (response === null || !response.ok) {
|
||||||
|
throw new Error('Fail!')
|
||||||
|
}
|
||||||
|
return response.json()
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} request_body
|
||||||
|
* @param {Boolean} needPut
|
||||||
|
*/
|
||||||
|
'_buildRequestOptions': (request_body, needPut) => {
|
||||||
|
return {
|
||||||
|
method: (needPut === true) ? 'PUT' : 'POST',
|
||||||
|
mode: 'cors',
|
||||||
|
cache: 'no-cache',
|
||||||
|
credentials: 'omit',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: request_body,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} rest_url
|
||||||
|
* @param {Object} request_options
|
||||||
|
*/
|
||||||
|
'_pureFetch': async (rest_url, request_options = {}) => {
|
||||||
|
// noinspection JSUnresolvedVariable
|
||||||
|
return await fetch(rest_url, request_options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WorkingHoursRepository = {
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
* @param {String} startDate
|
||||||
|
* @param {String} endDate
|
||||||
|
*/
|
||||||
|
'browse': (api_url, startDate = '', endDate = '') => {
|
||||||
|
let filter = {}
|
||||||
|
if (startDate !== '') {
|
||||||
|
filter['start-date'] = startDate
|
||||||
|
}
|
||||||
|
if (endDate !== '') {
|
||||||
|
filter['end-date'] = endDate
|
||||||
|
}
|
||||||
|
return MyFetch.get(api_url + '/working-hours', filter)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
* @param {String} date
|
||||||
|
*/
|
||||||
|
'read': (api_url, date) => {
|
||||||
|
return MyFetch.get(api_url + '/working-hours/' + date)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
* @param {String} date
|
||||||
|
*/
|
||||||
|
'readOrNew': async (api_url, date) => {
|
||||||
|
let response = await MyFetch._pureFetch(api_url + '/working-hours/' + date)
|
||||||
|
if (response.status === 404) {
|
||||||
|
return {workingTime: '00:00:00', workingDay: date}
|
||||||
|
} else if (!response.ok) {
|
||||||
|
throw new Error('Fail!')
|
||||||
|
}
|
||||||
|
return response.json()
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
* @param {Object} record
|
||||||
|
*/
|
||||||
|
'add': async (api_url, record) => {
|
||||||
|
return MyFetch.post(api_url + '/working-hours', JSON.stringify(record))
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
* @param {String} date
|
||||||
|
* @param {Object} record
|
||||||
|
*/
|
||||||
|
'update': async (api_url, date, record) => {
|
||||||
|
return MyFetch.put(api_url + '/working-hours/' + date, JSON.stringify(record))
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
* @param {String} date
|
||||||
|
* @param {Object} record
|
||||||
|
*/
|
||||||
|
'addOrUpdate': async (api_url, date, record) => {
|
||||||
|
let request_options = MyFetch._buildRequestOptions(JSON.stringify(record), false)
|
||||||
|
let response = await MyFetch._pureFetch(api_url + '/working-hours', request_options)
|
||||||
|
if (response.status === 409) {
|
||||||
|
return WorkingHoursRepository.update(api_url, date, record)
|
||||||
|
} else if (!response.ok) {
|
||||||
|
throw new Error('Fail!')
|
||||||
|
}
|
||||||
|
return response.json()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WorkingHoursViewsRepository = {
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
*/
|
||||||
|
'weekly': async api_url => {
|
||||||
|
return MyFetch.get(api_url + '/views/working-hours/weekly', {})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
*/
|
||||||
|
'monthly': async api_url => {
|
||||||
|
return MyFetch.get(api_url + '/views/working-hours/monthly', {})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
*/
|
||||||
|
'yearly': async api_url => {
|
||||||
|
return MyFetch.get(api_url + '/views/working-hours/yearly', {})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<script>
|
||||||
|
import {PeriodRecord} from './Models.svelte.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {{ promise: Promise<object>; title: string;}}
|
||||||
|
*/
|
||||||
|
let {params} = $props();
|
||||||
|
/** @type PeriodRecord[] **/
|
||||||
|
let records = $state([])
|
||||||
|
let isLoading = $state(true)
|
||||||
|
const init = async () => await params.promise.then(
|
||||||
|
/**
|
||||||
|
* @param {Array<object>} data
|
||||||
|
*/
|
||||||
|
data => {
|
||||||
|
records = data.map(row => new PeriodRecord(row))
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
init()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{params.title}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<section id="list-records">
|
||||||
|
<header>
|
||||||
|
<h1>{params.title}</h1>
|
||||||
|
</header>
|
||||||
|
<article>
|
||||||
|
{#if isLoading === true}
|
||||||
|
<p>Loading...</p>
|
||||||
|
{:else}
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Zeitraum</th>
|
||||||
|
<th>Arbeitstage</th>
|
||||||
|
<th>Arbeitsstunden</th>
|
||||||
|
<th>Über- / Unterstunden</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each records as record}
|
||||||
|
<tr>
|
||||||
|
<td>{record.period}</td>
|
||||||
|
<td>{record.workingDays}</td>
|
||||||
|
<td>{record.totalHours}</td>
|
||||||
|
<td class="{record.isMinus ? 'absence-time' : ''}">{record.overtime}</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{/if}
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<script>
|
||||||
|
import { page } from '$app/state';
|
||||||
|
let { children } = $props();
|
||||||
|
/** @param {String} str **/
|
||||||
|
const isActive = str => page.url.pathname === str;
|
||||||
|
/** @param {String} str **/
|
||||||
|
const isSelected = str => isActive(str) ? 'selected' : '';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li class="{isSelected('/')}"><a href="/" aria-current={isActive('/')}>Home</a></li>
|
||||||
|
<li class="{isSelected('/views/weekly')}"><a href="/views/weekly" aria-current={isActive('/views/weekly')}>Wochenansicht</a></li>
|
||||||
|
<li class="{isSelected('/views/monthly')}"><a href="/views/monthly" aria-current={isActive('/views/monthly')}>Monatsansicht</a></li>
|
||||||
|
<li class="{isSelected('/views/yearly')}"><a href="/views/yearly" aria-current={isActive('/views/yearly')}>Jahresansicht</a></li>
|
||||||
|
<li class="{isSelected('/working-hours')}"><a href="/working-hours" aria-current={isActive('/working-hours')}>Einträge</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
{@render children()}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer><p>© Torsten Lücke 2021</p></footer>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {env} from '$env/dynamic/private';
|
||||||
|
import {WorkingHoursViewsRepository} from "../lib/Repositories.svelte";
|
||||||
|
|
||||||
|
export function load() {
|
||||||
|
return {
|
||||||
|
title: 'Wochenansicht',
|
||||||
|
promise: WorkingHoursViewsRepository.weekly(env.API_URL)
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<script>
|
||||||
|
import Views from "$lib/Views.svelte";
|
||||||
|
let { data } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Views params={data}/>
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {env} from '$env/dynamic/private';
|
||||||
|
import {WorkingHoursViewsRepository} from "$lib/Repositories.svelte";
|
||||||
|
|
||||||
|
export function load() {
|
||||||
|
return {
|
||||||
|
title: 'Monatsansicht',
|
||||||
|
promise: WorkingHoursViewsRepository.monthly(env.API_URL)
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<script>
|
||||||
|
import Views from "$lib/Views.svelte";
|
||||||
|
let { data } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Views params={data}/>
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {env} from '$env/dynamic/private';
|
||||||
|
import {WorkingHoursViewsRepository} from "$lib/Repositories.svelte";
|
||||||
|
|
||||||
|
export function load() {
|
||||||
|
return {
|
||||||
|
title: 'Wochenansicht',
|
||||||
|
promise: WorkingHoursViewsRepository.weekly(env.API_URL)
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<script>
|
||||||
|
import Views from "$lib/Views.svelte";
|
||||||
|
let { data } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Views params={data}/>
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {env} from '$env/dynamic/private';
|
||||||
|
import {WorkingHoursViewsRepository} from "$lib/Repositories.svelte";
|
||||||
|
|
||||||
|
export function load() {
|
||||||
|
return {
|
||||||
|
title: 'Jahresansicht',
|
||||||
|
promise: WorkingHoursViewsRepository.yearly(env.API_URL)
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<script>
|
||||||
|
import Views from "$lib/Views.svelte";
|
||||||
|
let { data } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Views params={data}/>
|
||||||
|
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import {env} from '$env/dynamic/private';
|
||||||
|
import {WorkingHoursRepository} from "$lib/Repositories.svelte";
|
||||||
|
|
||||||
|
export function load() {
|
||||||
|
return {
|
||||||
|
promise: WorkingHoursRepository.browse(env.API_URL)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {Date} actualDayObject **/
|
||||||
|
const getMonday = actualDayObject => {
|
||||||
|
if (actualDayObject.getDate() === 0) {
|
||||||
|
actualDayObject.setDate(actualDayObject.getDate() - 7)
|
||||||
|
}
|
||||||
|
if (actualDayObject.getDate() !== 1) {
|
||||||
|
actualDayObject.setDate(actualDayObject.getDate() - (actualDayObject.getDay() - 1))
|
||||||
|
}
|
||||||
|
return actualDayObject
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {String[]} **/
|
||||||
|
const weekday_list = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sontag']
|
||||||
|
|
||||||
|
/** @param {Date} actualDayObject **/
|
||||||
|
const getDateString = actualDayObject => {
|
||||||
|
return actualDayObject.getFullYear() + "-"
|
||||||
|
+ (actualDayObject.getMonth() + 1).toString().padStart(2, '0') + "-"
|
||||||
|
+ actualDayObject.getDate().toString().padStart(2, '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const actions = {
|
||||||
|
update: async ({ request }) => {
|
||||||
|
const data = await request.formData();
|
||||||
|
const body_data = {
|
||||||
|
workingDay: data.get('workingDay'),
|
||||||
|
workingTime: data.get('workingTime'),
|
||||||
|
}
|
||||||
|
await WorkingHoursRepository.update(env.API_URL, data.get('workingDay'), body_data);
|
||||||
|
},
|
||||||
|
create: async ({ request }) => {
|
||||||
|
const form_data = await request.formData();
|
||||||
|
const body_data = {
|
||||||
|
workingDay: form_data.get('workingDay'),
|
||||||
|
workingTime: form_data.get('workingTime'),
|
||||||
|
}
|
||||||
|
await WorkingHoursRepository.add(env.API_URL, body_data);
|
||||||
|
},
|
||||||
|
"create-by-week": async ({ request }) => {
|
||||||
|
const form_data = await request.formData();
|
||||||
|
// @ts-ignore
|
||||||
|
let actualDayObject = getMonday(new Date(form_data.get('week')))
|
||||||
|
/** @type {Object<String,String>} **/
|
||||||
|
let day_list = {}
|
||||||
|
weekday_list.forEach(day => {
|
||||||
|
day_list[day] = getDateString(actualDayObject)
|
||||||
|
actualDayObject.setDate(actualDayObject.getDate() + 1)
|
||||||
|
})
|
||||||
|
for (const pair of form_data.entries()) {
|
||||||
|
const working_day = day_list[pair[0]] ?? null
|
||||||
|
if (working_day === null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const body_data = {
|
||||||
|
workingDay: working_day,
|
||||||
|
workingTime: pair[1],
|
||||||
|
}
|
||||||
|
await WorkingHoursRepository.addOrUpdate(env.API_URL, working_day, body_data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'import-csv': async ({ request }) => {
|
||||||
|
const form_data = await request.formData();
|
||||||
|
for (const pair of form_data.entries()) {
|
||||||
|
const working_day = pair[0]
|
||||||
|
const body_data = {
|
||||||
|
workingDay: working_day,
|
||||||
|
workingTime: pair[1],
|
||||||
|
}
|
||||||
|
await WorkingHoursRepository.addOrUpdate(env.API_URL, working_day, body_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<script>
|
||||||
|
import documentIcon from "@iconify-icons/oi/document.js";
|
||||||
|
import wrenchIcon from "@iconify-icons/oi/wrench.js";
|
||||||
|
import IconifyIcon from "@iconify/svelte";
|
||||||
|
let { data } = $props();
|
||||||
|
const TITLE = "Bearbeitung Einträge"
|
||||||
|
/** @type {{workingDay: String, workingTime: String, }[]|null} **/
|
||||||
|
let records = $state([])
|
||||||
|
const init = async () => await data.promise.then(
|
||||||
|
/**
|
||||||
|
* @param {{workingDay: String, workingTime: String, }[]} data
|
||||||
|
*/
|
||||||
|
data => {
|
||||||
|
records = data
|
||||||
|
}
|
||||||
|
)
|
||||||
|
init()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{TITLE}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<section id="list-records">
|
||||||
|
<header>
|
||||||
|
<h1>{TITLE}</h1>
|
||||||
|
</header>
|
||||||
|
<article>
|
||||||
|
<!--suppress HtmlUnknownTarget -->
|
||||||
|
<form action="/working-hours">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Neuer Eintrag</legend>
|
||||||
|
<button formaction="/working-hours/create-by-day" type="submit">per Tag
|
||||||
|
<IconifyIcon icon={documentIcon}/>
|
||||||
|
</button>
|
||||||
|
<button formaction="/working-hours/create-by-week" type="submit">per Woche
|
||||||
|
<IconifyIcon icon={documentIcon}/>
|
||||||
|
</button>
|
||||||
|
<button formaction="/working-hours/import-csv" type="submit">Import per CSV
|
||||||
|
<IconifyIcon icon={documentIcon}/>
|
||||||
|
</button>
|
||||||
|
</fieldset>
|
||||||
|
{#if records === null}
|
||||||
|
<p>Loading...</p>
|
||||||
|
{:else}
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Datum</th>
|
||||||
|
<th>Arbeitsstunden</th>
|
||||||
|
<th>Aktionen</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each records as record}
|
||||||
|
<tr>
|
||||||
|
<td>{record.workingDay}</td>
|
||||||
|
<td>{record.workingTime}</td>
|
||||||
|
<td>
|
||||||
|
<button formaction={'/working-hours/' + record.workingDay} type="submit">Bearbeiten
|
||||||
|
<IconifyIcon icon={wrenchIcon}/>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{/if}
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {env} from '$env/dynamic/private';
|
||||||
|
import {WorkingHoursRepository} from "$lib/Repositories.svelte";
|
||||||
|
|
||||||
|
export function load({ params }) {
|
||||||
|
return {
|
||||||
|
working_day: params.working_day,
|
||||||
|
promise: WorkingHoursRepository.read(env.API_URL, params.working_day)
|
||||||
|
};
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user