8 Commits
11 changed files with 140 additions and 306 deletions
+2 -1
View File
@@ -91,12 +91,13 @@ class WorkingHoursView implements ModelInterface
'overtime' => "string" 'overtime' => "string"
])] public function jsonSerialize(): array ])] 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()->getValue()),
'periodDesignation' => strtolower($this->getPeriodDesignation()->getKey()), 'periodDesignation' => strtolower($this->getPeriodDesignation()->getKey()),
'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)
]; ];
} }
} }
+20 -1
View File
@@ -12,6 +12,9 @@ use Psr\Http\Message\StreamInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Slim\App; use Slim\App;
use Slim\Exception\HttpException; use Slim\Exception\HttpException;
use Slim\Interfaces\RouteParserInterface;
use Slim\Routing\RouteContext;
use Slim\Routing\RoutingResults;
use TorstenHettstedt\TimekeepingApi\Middleware\ErrorHandler; use TorstenHettstedt\TimekeepingApi\Middleware\ErrorHandler;
class ErrorHandlerTest extends Unit class ErrorHandlerTest extends Unit
@@ -28,6 +31,7 @@ class ErrorHandlerTest extends Unit
protected function _before(): void protected function _before(): void
{ {
parent::_before(); parent::_before();
/** @noinspection PhpFieldAssignmentTypeMismatchInspection */
$this->app = $this->makeEmpty(App::class, [ $this->app = $this->makeEmpty(App::class, [
'getResponseFactory' => $this->makeEmpty(ResponseFactoryInterface::class, [ 'getResponseFactory' => $this->makeEmpty(ResponseFactoryInterface::class, [
'createResponse' => $this->makeEmpty(ResponseInterface::class, [ 'createResponse' => $this->makeEmpty(ResponseInterface::class, [
@@ -40,7 +44,16 @@ class ErrorHandlerTest extends Unit
]), ]),
]), ]),
]); ]);
$this->request = $this->makeEmpty(ServerRequestInterface::class); /** @noinspection PhpFieldAssignmentTypeMismatchInspection */
$this->request = $this->makeEmpty(ServerRequestInterface::class, [
'getAttribute' => function($name) {
return match ($name) {
RouteContext::ROUTE_PARSER => $this->makeEmpty(RouteParserInterface::class),
RouteContext::ROUTING_RESULTS => $this->makeEmpty(RoutingResults::class),
default => null,
};
}
]);
} }
/** /**
@@ -48,11 +61,13 @@ class ErrorHandlerTest extends Unit
*/ */
public function testUseWithLogger(): void public function testUseWithLogger(): void
{ {
/** @noinspection PhpFieldAssignmentTypeMismatchInspection */
$this->exception = $this->make(Exception::class, [ $this->exception = $this->make(Exception::class, [
'message' => '', 'message' => '',
'code' => 400, 'code' => 400,
'file' => '/path(to/file', 'file' => '/path(to/file',
]); ]);
/** @noinspection PhpFieldAssignmentTypeMismatchInspection */
$this->logger = $this->makeEmpty(LoggerInterface::class, [ $this->logger = $this->makeEmpty(LoggerInterface::class, [
'error' => Expected::once(), 'error' => Expected::once(),
]); ]);
@@ -66,12 +81,14 @@ class ErrorHandlerTest extends Unit
*/ */
public function testUseWithHttpException(): void public function testUseWithHttpException(): void
{ {
/** @noinspection PhpFieldAssignmentTypeMismatchInspection */
$this->exception = $this->make(HttpException::class, [ $this->exception = $this->make(HttpException::class, [
'message' => '', 'message' => '',
'code' => 400, 'code' => 400,
'file' => '/path(to/file', 'file' => '/path(to/file',
'getTitle' => Expected::once('The Title'), 'getTitle' => Expected::once('The Title'),
]); ]);
/** @noinspection PhpFieldAssignmentTypeMismatchInspection */
$this->logger = $this->makeEmpty(LoggerInterface::class, []); $this->logger = $this->makeEmpty(LoggerInterface::class, []);
$middleWare = new ErrorHandler($this->app); $middleWare = new ErrorHandler($this->app);
@@ -84,11 +101,13 @@ class ErrorHandlerTest extends Unit
*/ */
public function testUseWithGeneralException(): void public function testUseWithGeneralException(): void
{ {
/** @noinspection PhpFieldAssignmentTypeMismatchInspection */
$this->exception = $this->make(Exception::class, [ $this->exception = $this->make(Exception::class, [
'message' => '', 'message' => '',
'code' => 400, 'code' => 400,
'file' => '/path(to/file', 'file' => '/path(to/file',
]); ]);
/** @noinspection PhpFieldAssignmentTypeMismatchInspection */
$this->logger = $this->makeEmpty(LoggerInterface::class, []); $this->logger = $this->makeEmpty(LoggerInterface::class, []);
$middleWare = new ErrorHandler($this->app); $middleWare = new ErrorHandler($this->app);
+37 -15
View File
@@ -17,13 +17,15 @@ class WorkingHoursViewTest extends Unit
*/ */
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(), $work_days, $total_hours, $overtime,
PeriodDesignationEnum::WEEKLY(),
15,
new DateInterval("PT32H22M"),
new DateInterval("PT22M"),
[ [
'period' => '2020#49', 'period' => '2020#49',
'periodDesignation' => 'weekly', 'periodDesignation' => 'weekly',
@@ -33,11 +35,17 @@ class WorkingHoursViewTest extends Unit
], ],
], ],
[ [
new DateTime('2020-11-30'), $date, PeriodDesignationEnum::WEEKLY(), $work_days, $total_hours, $absence_time,
PeriodDesignationEnum::MONTHLY(), [
15, 'period' => '2020#49',
new DateInterval("PT32H22M"), 'periodDesignation' => 'weekly',
new DateInterval("PT22M"), '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,12 +55,17 @@ class WorkingHoursViewTest extends Unit
], ],
], ],
[ [
new DateTime('2020-11-30'), $date, PeriodDesignationEnum::MONTHLY(), $work_days, $total_hours, $absence_time,
PeriodDesignationEnum::YEARLY(),
15,
new DateInterval("PT32H22M"),
new DateInterval("PT22M"),
[ [
'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',
'workingDays' => 15, 'workingDays' => 15,
@@ -60,6 +73,15 @@ 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'
],
],
]; ];
} }
-9
View File
@@ -71,15 +71,6 @@ section article {
font-size: 1.30em; font-size: 1.30em;
font-weight: bold; font-weight: bold;
} }
div.zelle > label {
}
}
}
&.http-error {
header {
background: hsl(340, 90%, 50%);
color: hsl(0, 0%, 95%);
} }
} }
} }
+1
View File
@@ -6,6 +6,7 @@ Definition der Farben
@hueSub: 215; @hueSub: 215;
@mainColor: hsl(@hueMain, 90%, 29%); @mainColor: hsl(@hueMain, 90%, 29%);
@redColor: hsl(0, 90%, 29%);
@mainColorDark: darken(@mainColor,66%,relativ); @mainColorDark: darken(@mainColor,66%,relativ);
@mainColorLight: lighten(@mainColor,66%,relativ); @mainColorLight: lighten(@mainColor,66%,relativ);
@subColor: hsl(@hueSub, 90%, 29%); @subColor: hsl(@hueSub, 90%, 29%);
+8 -90
View File
@@ -72,18 +72,6 @@ main {
margin: @sectionMargin; margin: @sectionMargin;
text-indent: @sectionMargin * 2; text-indent: @sectionMargin * 2;
} }
ul, ol {
margin: initial;
li {
margin: initial;
padding: initial;
}
}
dl {
dd {
margin-left: 2em;
}
}
table { table {
thead { thead {
tr:last-child { tr:last-child {
@@ -91,6 +79,13 @@ main {
border-bottom: @mainColor solid thin; border-bottom: @mainColor solid thin;
} }
} }
};
tbody {
td.absence-time {
color: @redColor;
text-decoration-line: underline;
text-decoration-style: dashed;
}
} }
} }
form { form {
@@ -116,7 +111,7 @@ main {
display: inline-block; display: inline-block;
margin: 0.5rem; margin: 0.5rem;
} }
input, textarea { input {
display: inline-block; display: inline-block;
width: @formItemWidth; width: @formItemWidth;
padding: 0.25rem; padding: 0.25rem;
@@ -124,38 +119,6 @@ main {
border-style: solid; border-style: solid;
border-width: thin; 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;
}
}
}
} }
} }
} }
@@ -188,48 +151,3 @@ main {
} }
} }
} }
aside {
background: @mainColorLight ;
li {
background: @mainColorLight;
color: @mainColor;
font-weight: bold ;
&:not(:first-child) {
margin-top: 1rem;
}
a {
display: block;
padding: 1.25rem .5rem;
text-decoration: none;
color: @mainColor;
&:hover, &:active, &:focus {
color: darken(@mainColor,10%,relative);
}
}
&:hover, &:active, &:focus {
background: lighten(@mainColorLight,10%,relative);
}
}
form {
@paddingForm: 0.5rem;
@paddingInput: @paddingForm/2;
background: @mainColorLight;
color: @mainColor;
padding: 2 * @paddingForm @paddingForm;
input {
display: block;
width: @breiteNavBereich - (@paddingForm + @paddingInput) * 2 ;
border: none;
margin-bottom: @paddingForm;
padding: @paddingInput;
}
}
p {
line-height: 1.5em;
text-align: left;
.hyphens();
margin: 1em;
font-size: 85%;
}
}
+43 -4
View File
@@ -137,6 +137,7 @@
width: @breiteNavBereich; width: @breiteNavBereich;
} }
} }
@media screen and (max-width: 830px) { @media screen and (max-width: 830px) {
html body { html body {
@boxHeight: 5rem; @boxHeight: 5rem;
@@ -145,16 +146,18 @@
font-size: @boxHeight; font-size: @boxHeight;
text-align: center; text-align: center;
padding: 1rem; padding: 1rem;
}
& > footer {
padding: 1rem;
} }
& > nav { & > nav {
background: none;
left:auto;
ul { ul {
li { li {
display: inline-block; display: inline-block;
border: none; border: none;
width: auto; width: auto;
height: 1.5em;
padding-top: 0.25em;
a { a {
padding: 0.75rem; padding: 0.75rem;
} }
@@ -184,6 +187,7 @@
@formLabelWidth: 10rem; @formLabelWidth: 10rem;
@formItemWidth: 40rem; @formItemWidth: 40rem;
@sectionMargin: 1rem; @sectionMargin: 1rem;
margin: @sectionMargin;
fieldset { fieldset {
width: auto; width: auto;
margin: 1rem auto; margin: 1rem auto;
@@ -262,6 +266,41 @@
} }
} }
} }
@media screen and (max-width: 720px) { @media screen and (max-width: 720px) {
html body {
@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();
}
}
}
}
} }
+7 -50
View File
@@ -1,14 +1,3 @@
.clearfix() {
&:after {
content: ".";
display: block;
height: 0;
line-height: 0;
clear: both;
visibility: hidden;
}
}
.hyphens() { .hyphens() {
-webkit-hyphens: auto; -webkit-hyphens: auto;
-moz-hyphens: auto; -moz-hyphens: auto;
@@ -59,27 +48,22 @@
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.mywrap() {
white-space: normal;
}
.hamburgerMenu() { .hamburgerMenu() {
color: white; color: white;
width: 3rem; width: 3rem;
height: 2rem; height: 2rem;
top: 0.5rem; top: 0.25rem;
right: 0.5rem; left: 0.25rem;
left: auto;
.border-radius(4px); .border-radius(4px);
border: none; border: none;
.vertical-gradient(#555; #000); .vertical-gradient(@mainColorDark; @mainColor);
> ul { > ul {
height: auto; height: auto;
width: auto; width: auto;
position: absolute; position: absolute;
right: 0; left: 0;
top: 100%; top: 100%;
background: fade(black,80%); background: fade(@mainColorDark,80%);
.border-radius(5px); .border-radius(5px);
display: none; display: none;
> li { > li {
@@ -100,7 +84,7 @@
&:hover, &:hover,
&:focus { &:focus {
color: white; color: white;
.vertical-gradient(#59b4fd; #0560e8); .vertical-gradient(@mainColorLight; @mainColor);
} }
} }
&:first-child { &:first-child {
@@ -115,7 +99,7 @@
} }
} }
} }
&:hover, &:focus, &:active { &:hover, &:focus, &:active, &:checked {
ul { ul {
display: block; display: block;
} }
@@ -130,19 +114,6 @@
} }
} }
.transition(@property) {
-moz-transition: ~"@{property}";
-webkit-transition: ~"@{property}";
-o-transition: ~"@{property}";
-ms-transition: ~"@{property}";
transition: ~"@{property}";
-moz-transition-duration: 0.5s;
-webkit-transition-duration: 0.5s;
-o-transition-duration: 0.5s;
-ms-transition-duration: 0.5s;
transition-duration: 0.5s;
}
.flexEltern() { .flexEltern() {
display:-ms-flexbox; display:-ms-flexbox;
display: flexbox; display: flexbox;
@@ -172,17 +143,3 @@
flex: @num; flex: @num;
flex-grow: @num; flex-grow: @num;
} }
.background-size(...) {
-webkit-background-size: @arguments;
-moz-background-size: @arguments;
background-size: @arguments;
}
.transform(...) {
-ms-transform: @arguments;
-webkit-transform: @arguments;
-o-transform: @arguments;
-moz-transform: @arguments;
transform: @arguments;
}
+1 -4
View File
@@ -75,9 +75,6 @@ section article {
} }
} }
&.http-error { &.http-error {
header { display: none;
background: hsl(340, 90%, 50%);
color: hsl(0, 0%, 95%);
}
} }
} }
+6 -125
View File
@@ -21,17 +21,15 @@ html {
margin: 1rem; margin: 1rem;
} }
} }
> nav {
display: none;
}
> footer { > footer {
color: contrast(@mainColor); color: contrast(@mainColor);
} }
} }
} }
#inhalts-bereich {
background-color: @mainColorLight;
color: @mainColor;
}
main { main {
* { * {
border-style: none; border-style: none;
@@ -46,22 +44,10 @@ main {
h1, h2 { h1, h2 {
margin: @sectionMargin; margin: @sectionMargin;
} }
p, ul, ol, dl { p {
margin: @sectionMargin; margin: @sectionMargin;
text-indent: @sectionMargin * 2; text-indent: @sectionMargin * 2;
} }
ul, ol {
margin: initial;
li {
margin: initial;
padding: initial;
}
}
dl {
dd {
margin-left: 2em;
}
}
table { table {
colgroup:not(:first-child) col:first-child{ colgroup:not(:first-child) col:first-child{
border-left: solid medium; border-left: solid medium;
@@ -86,70 +72,10 @@ main {
} }
} }
form { form {
@formLabelWidth: 10rem; display: none;
@formItemWidth: 40rem;
fieldset {
width: @formItemWidth + @formLabelWidth + 2rem;
margin: 1rem auto;
border-style: solid ;
padding: @sectionMargin;
&.tasten {
border: none;
background-color: @mainColorLight;
}
legend {
margin: @sectionMargin;
margin-left: @sectionMargin * 2;
}
}
div.zelle {
& > label {
width: @formLabelWidth;
display: inline-block;
margin: 0.5rem;
}
input, textarea {
display: inline-block;
width: @formItemWidth;
padding: 0.25rem;
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;
}
} }
button { button {
@farbeHintergrund: @mainColorLight; display: none;
@farbeHintergrundHell: lighten(@farbeHintergrund,50%,relativ);
@farbeText: spin(lighten(@farbeHintergrund,66%,relativ),@mainColor);
@farbeSchatten: fade(@farbeHintergrund,50%);
height: 2.5em;
.border-radius(1.25em);
padding: 0.5em 1.25em;
.vertical-gradient(@farbeHintergrundHell,@farbeHintergrund);
.box-shadow(0.1em 0.2em 0.5em @farbeSchatten);
color: @farbeText;
font-weight: bold;
&:hover {
.vertical-gradient(@farbeHintergrund,@farbeHintergrundHell);
}
margin: 0.25rem;
}
} }
footer { footer {
text-align: right; text-align: right;
@@ -171,48 +97,3 @@ main {
} }
} }
} }
aside {
background: @subColorLight ;
li {
background: @mainColorLight;
color: @mainColor;
font-weight: bold ;
&:not(:first-child) {
margin-top: 1rem;
}
a {
display: block;
padding: 1.25rem .5rem;
text-decoration: none;
color: @mainColor;
&:hover, &:active, &:focus {
color: darken(@mainColor,10%,relative);
}
}
&:hover, &:active, &:focus {
background: lighten(@mainColor,10%,relative);
}
}
form {
@paddingForm: 0.5rem;
@paddingInput: @paddingForm/2;
background: @mainColorLight;
color: @mainColor;
padding: 2 * @paddingForm @paddingForm;
input {
display: block;
width: @mainColor - (@paddingForm + @paddingInput) * 2 ;
border: none;
margin-bottom: @paddingForm;
padding: @paddingInput;
}
}
p {
line-height: 1.5em;
text-align: left;
.hyphens();
margin: 1em;
font-size: 85%;
}
}
+9 -1
View File
@@ -1,6 +1,14 @@
<script> <script>
export let params; export let params;
console.log(params) console.log(params)
if (!String.prototype.startsWith) {
String.prototype.startsWith = function(searchString, position) {
position = position || 0;
return this.indexOf(searchString, position) === position;
};
}
$: isNegative = time => time.startsWith('-') ? 'absence-time' : '';
$: cutMinusChar = time => time.startsWith('-') ? time.substr(1) : time;
</script> </script>
<svelte:head> <svelte:head>
@@ -36,7 +44,7 @@
<td>{record.period}</td> <td>{record.period}</td>
<td>{record.workingDays}</td> <td>{record.workingDays}</td>
<td>{record.totalHours}</td> <td>{record.totalHours}</td>
<td>{record.overtime}</td> <td class="{ isNegative(record.overtime) }">{cutMinusChar(record.overtime)}</td>
</tr> </tr>
{/each} {/each}
</tbody> </tbody>