Compare commits
11
Commits
532c0d60e8
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcce40113d | ||
|
|
0e1244c02f | ||
|
|
b4d30066b9 | ||
|
|
ab9bccf532 | ||
|
|
4acf1275a0 | ||
|
|
6fb8f87c1c | ||
|
|
1d53db71e7 | ||
|
|
dbca8ad570 | ||
|
|
4d91a04430 | ||
|
|
2dcbb6fdbd | ||
|
|
9f53b627c0 |
@@ -21,12 +21,6 @@ class WorkingHoursController extends AbstractController
|
|||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
|
||||||
* @param Response $response
|
|
||||||
* @param array<string, mixed> $args
|
|
||||||
*
|
|
||||||
* @return Response
|
|
||||||
*
|
|
||||||
* @throws HttpBadRequestException
|
* @throws HttpBadRequestException
|
||||||
* @throws HttpNotFoundException
|
* @throws HttpNotFoundException
|
||||||
*/
|
*/
|
||||||
@@ -34,7 +28,7 @@ class WorkingHoursController extends AbstractController
|
|||||||
{
|
{
|
||||||
$body = $request->getParsedBody();
|
$body = $request->getParsedBody();
|
||||||
$repository = new WorkingHoursRepository($this->databases);
|
$repository = new WorkingHoursRepository($this->databases);
|
||||||
$model = $this->buildModel($request, $args['id'], $body['workingTime'], $body['isHomeOfficeDay'] ?? false);
|
$model = $this->buildModel($request, $args['id'], $body['workingTime'], $body['isHomeOffice'] ?? false);
|
||||||
try {
|
try {
|
||||||
$repository->update($model);
|
$repository->update($model);
|
||||||
} catch (RepositoryRecordNotFoundException $exception) {
|
} catch (RepositoryRecordNotFoundException $exception) {
|
||||||
@@ -44,12 +38,6 @@ class WorkingHoursController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
|
||||||
* @param Response $response
|
|
||||||
* @param array<string, mixed> $args
|
|
||||||
*
|
|
||||||
* @return Response
|
|
||||||
*
|
|
||||||
* @throws HttpInternalServerErrorException
|
* @throws HttpInternalServerErrorException
|
||||||
* @throws HttpBadRequestException
|
* @throws HttpBadRequestException
|
||||||
* @noinspection PhpUnusedParameterInspection
|
* @noinspection PhpUnusedParameterInspection
|
||||||
@@ -79,12 +67,6 @@ class WorkingHoursController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
|
||||||
* @param Response $response
|
|
||||||
* @param array<string, mixed> $args
|
|
||||||
*
|
|
||||||
* @return Response
|
|
||||||
*
|
|
||||||
* @throws HttpNotFoundException
|
* @throws HttpNotFoundException
|
||||||
* @throws HttpInternalServerErrorException
|
* @throws HttpInternalServerErrorException
|
||||||
*/
|
*/
|
||||||
@@ -105,12 +87,6 @@ class WorkingHoursController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
|
||||||
* @param Response $response
|
|
||||||
* @param array<string, mixed> $args
|
|
||||||
*
|
|
||||||
* @return Response
|
|
||||||
*
|
|
||||||
* @throws HttpBadRequestException
|
* @throws HttpBadRequestException
|
||||||
* @throws HttpConflictRequestException
|
* @throws HttpConflictRequestException
|
||||||
* @throws HttpInternalServerErrorException
|
* @throws HttpInternalServerErrorException
|
||||||
@@ -124,7 +100,7 @@ class WorkingHoursController extends AbstractController
|
|||||||
$request,
|
$request,
|
||||||
$body['workingDay'],
|
$body['workingDay'],
|
||||||
$body['workingTime'],
|
$body['workingTime'],
|
||||||
$body['isHomeOfficeDay'] ?? false
|
$body['isHomeOffice'] ?? false
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
$repository->insert($model);
|
$repository->insert($model);
|
||||||
@@ -141,11 +117,6 @@ class WorkingHoursController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
|
||||||
* @param string $date
|
|
||||||
* @param string $time
|
|
||||||
*
|
|
||||||
* @return WorkingHours
|
|
||||||
* @throws HttpBadRequestException
|
* @throws HttpBadRequestException
|
||||||
*/
|
*/
|
||||||
protected function buildModel(Request $request, string $date, string $time, bool $isHomeOffice): WorkingHours
|
protected function buildModel(Request $request, string $date, string $time, bool $isHomeOffice): WorkingHours
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ use TorstenHettstedt\TimekeepingApi\Models\WorkingHoursView;
|
|||||||
abstract class AbstractWorkingHoursViewRepository implements RepositoryReaderInterface
|
abstract class AbstractWorkingHoursViewRepository implements RepositoryReaderInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
protected const SQL_SELECT = '';
|
protected const string SQL_SELECT = '';
|
||||||
protected const SQL_WHERE = '';
|
protected const string SQL_WHERE = '';
|
||||||
|
|
||||||
protected PeriodDesignationEnum $periodDesignation;
|
protected PeriodDesignationEnum $periodDesignation;
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ 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",
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class Api extends Module
|
|||||||
"periodDesignation" => "string",
|
"periodDesignation" => "string",
|
||||||
"totalHours" => self::FORMAT_TIME,
|
"totalHours" => self::FORMAT_TIME,
|
||||||
"workingDays" => 'integer:>0',
|
"workingDays" => 'integer:>0',
|
||||||
'homeOfficeDays' => 'number:>=0',
|
'homeOfficeDays' => 'integer:>=0',
|
||||||
"overtime" => self::FORMAT_TIME,
|
"overtime" => self::FORMAT_TIME,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,21 @@ class CreateWorkingHoursCest
|
|||||||
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function createWorkingHoursWithNewValidRecordWithoutHomeOffice(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
|
// tests
|
||||||
public function createWorkingHoursWithNewInvalidRecord(ApiTester $I): void
|
public function createWorkingHoursWithNewInvalidRecord(ApiTester $I): void
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Controller;
|
|||||||
|
|
||||||
use Codeception\Attribute\DataProvider;
|
use Codeception\Attribute\DataProvider;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use PHPUnit\Framework\MockObject\Exception as MockException;
|
||||||
use Psr\Container\ContainerExceptionInterface;
|
use Psr\Container\ContainerExceptionInterface;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
use Psr\Container\NotFoundExceptionInterface;
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
@@ -25,7 +26,7 @@ class WorkingHoursControllerTest extends AbstractControllerTest
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
* @throws \PHPUnit\Framework\MockObject\Exception
|
* @throws MockException
|
||||||
*/
|
*/
|
||||||
protected function _before(): void
|
protected function _before(): void
|
||||||
{
|
{
|
||||||
@@ -34,7 +35,7 @@ class WorkingHoursControllerTest extends AbstractControllerTest
|
|||||||
'getParsedBody' => [
|
'getParsedBody' => [
|
||||||
'workingDay' => self::EXISTING_DATE,
|
'workingDay' => self::EXISTING_DATE,
|
||||||
'workingTime' => self::EXISTING_INTERVAL,
|
'workingTime' => self::EXISTING_INTERVAL,
|
||||||
'isHomeOfficeDay' => true,
|
'isHomeOffice' => true,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -316,9 +317,6 @@ class WorkingHoursControllerTest extends AbstractControllerTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $date
|
|
||||||
* @param string $time
|
|
||||||
*
|
|
||||||
* @throws ContainerExceptionInterface
|
* @throws ContainerExceptionInterface
|
||||||
* @throws NotDatabasesException
|
* @throws NotDatabasesException
|
||||||
* @throws NotFoundExceptionInterface
|
* @throws NotFoundExceptionInterface
|
||||||
@@ -331,7 +329,7 @@ class WorkingHoursControllerTest extends AbstractControllerTest
|
|||||||
'getParsedBody' => [
|
'getParsedBody' => [
|
||||||
'workingDay' => $date,
|
'workingDay' => $date,
|
||||||
'workingTime' => $time,
|
'workingTime' => $time,
|
||||||
'isHomeOfficeDay' => $isHomeOffice,
|
'isHomeOffice' => $isHomeOffice,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
$controller = new WorkingHoursController($this->container);
|
$controller = new WorkingHoursController($this->container);
|
||||||
@@ -351,7 +349,7 @@ class WorkingHoursControllerTest extends AbstractControllerTest
|
|||||||
'getParsedBody' => [
|
'getParsedBody' => [
|
||||||
'workingDay' => self::NEW_DATE,
|
'workingDay' => self::NEW_DATE,
|
||||||
'workingTime' => self::NEW_INTERVAL,
|
'workingTime' => self::NEW_INTERVAL,
|
||||||
'isHomeOfficeDay' => true,
|
'isHomeOffice' => true,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
$controller = new WorkingHoursController($this->container);
|
$controller = new WorkingHoursController($this->container);
|
||||||
@@ -392,7 +390,7 @@ class WorkingHoursControllerTest extends AbstractControllerTest
|
|||||||
'getParsedBody' => [
|
'getParsedBody' => [
|
||||||
'workingDay' => self::EXISTING_DATE,
|
'workingDay' => self::EXISTING_DATE,
|
||||||
'workingTime' => self::EXISTING_INTERVAL,
|
'workingTime' => self::EXISTING_INTERVAL,
|
||||||
'isHomeOfficeDay' => true,
|
'isHomeOffice' => true,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
$controller = new WorkingHoursController($this->container);
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ 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;
|
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;
|
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;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;
|
Verkehr;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;
|
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;
|
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;04:55;2021-07-20 07:37;2021-07-20 12:32;
|
||||||
|
|||||||
|
@@ -1,9 +1,10 @@
|
|||||||
export class PeriodRecord {
|
export class PeriodRecord {
|
||||||
/**
|
/**
|
||||||
* @param {{ workingDays: Number; totalHours: any; overtime: string; period: string; }} record
|
* @param {{ workingDays: Number; totalHours: any; overtime: string; period: string; homeOfficeDays: Number }} record
|
||||||
*/
|
*/
|
||||||
constructor(record) {
|
constructor(record) {
|
||||||
this.workingDays = Number(record.workingDays)
|
this.workingDays = Number(record.workingDays)
|
||||||
|
this.homeOfficeDays = Number(record.homeOfficeDays)
|
||||||
this.totalHours = String(record.totalHours)
|
this.totalHours = String(record.totalHours)
|
||||||
this.period = String(record.period)
|
this.period = String(record.period)
|
||||||
this.overtime = String('##:##:##')
|
this.overtime = String('##:##:##')
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
let {promise_data, actual_records= $bindable(), page_count = 10} = $props();
|
||||||
|
let records: Array<any[]> = $state([])
|
||||||
|
let page_number = $state(0)
|
||||||
|
let max_page_number = $state(0)
|
||||||
|
const setPage = (new_page: number) => {
|
||||||
|
page_number = new_page
|
||||||
|
actual_records = records[page_number - 1]
|
||||||
|
}
|
||||||
|
const init = async () => {
|
||||||
|
let page_records: any[] = []
|
||||||
|
console.log(promise_data, actual_records)
|
||||||
|
for (let index in promise_data) {
|
||||||
|
page_records.push(promise_data[index])
|
||||||
|
if (page_records.length >= page_count) {
|
||||||
|
records.push(page_records.values().toArray())
|
||||||
|
page_records = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (page_records.length > 0) {
|
||||||
|
records.push(page_records.values().toArray())
|
||||||
|
}
|
||||||
|
max_page_number = records.length
|
||||||
|
setPage(1)
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
div.table-pagination {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.table-pagination > button {
|
||||||
|
display: inline-block;
|
||||||
|
width: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.table-pagination > button[disabled] {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.table-pagination > span {
|
||||||
|
display: inline-block;
|
||||||
|
width: 5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="table-pagination">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={page_number === 1}
|
||||||
|
onclick={() => setPage(1)}>≪
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={page_number === 1}
|
||||||
|
onclick={() => setPage(page_number - 1)}><
|
||||||
|
</button>
|
||||||
|
<span>{page_number} von {max_page_number}</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={page_number === max_page_number}
|
||||||
|
onclick={() => setPage(page_number + 1)}>>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={page_number === max_page_number}
|
||||||
|
onclick={() => setPage(max_page_number)}>≫
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
+18
-9
@@ -1,16 +1,16 @@
|
|||||||
<script>
|
<script>
|
||||||
import {PeriodRecord} from './Models.svelte.js'
|
import {PeriodRecord} from './Models.svelte.js'
|
||||||
|
import TablePagination from "$lib/TablePagination.svelte";
|
||||||
|
|
||||||
/**
|
let { promise, title } = $props();
|
||||||
* @type {{ promise: Promise<object>; title: string;}}
|
|
||||||
*/
|
|
||||||
let {params} = $props();
|
|
||||||
/** @type PeriodRecord[] **/
|
/** @type PeriodRecord[] **/
|
||||||
let records = $state([])
|
let records = $state([])
|
||||||
|
/** @type PeriodRecord[] **/
|
||||||
|
let actual_records = $state([])
|
||||||
let isLoading = $state(true)
|
let isLoading = $state(true)
|
||||||
const init = async () => await params.promise.then(
|
const init = async () => await promise.then(
|
||||||
/**
|
/**
|
||||||
* @param {Array<object>} data
|
* @param {Array<{ workingDays: Number; totalHours: any; overtime: string; period: string; homeOfficeDays: Number; }>} data
|
||||||
*/
|
*/
|
||||||
data => {
|
data => {
|
||||||
records = data.map(row => new PeriodRecord(row))
|
records = data.map(row => new PeriodRecord(row))
|
||||||
@@ -21,12 +21,12 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>{params.title}</title>
|
<title>{title}</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<section id="list-records">
|
<section id="list-records">
|
||||||
<header>
|
<header>
|
||||||
<h1>{params.title}</h1>
|
<h1>{title}</h1>
|
||||||
</header>
|
</header>
|
||||||
<article>
|
<article>
|
||||||
{#if isLoading === true}
|
{#if isLoading === true}
|
||||||
@@ -43,20 +43,29 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Zeitraum</th>
|
<th>Zeitraum</th>
|
||||||
<th>Arbeitstage</th>
|
<th>Arbeitstage</th>
|
||||||
|
<th>davon HomeOffice</th>
|
||||||
<th>Arbeitsstunden</th>
|
<th>Arbeitsstunden</th>
|
||||||
<th>Über- / Unterstunden</th>
|
<th>Über- / Unterstunden</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each records as record}
|
{#each actual_records as record}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{record.period}</td>
|
<td>{record.period}</td>
|
||||||
<td>{record.workingDays}</td>
|
<td>{record.workingDays}</td>
|
||||||
|
<td>{record.homeOfficeDays}</td>
|
||||||
<td>{record.totalHours}</td>
|
<td>{record.totalHours}</td>
|
||||||
<td class="{record.isMinus ? 'absence-time' : ''}">{record.overtime}</td>
|
<td class="{record.isMinus ? 'absence-time' : ''}">{record.overtime}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">
|
||||||
|
<TablePagination promise_data={records} bind:actual_records={actual_records}/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
{/if}
|
{/if}
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -3,5 +3,5 @@
|
|||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Views params={data}/>
|
<Views {...data}/>
|
||||||
|
|
||||||
|
|||||||
@@ -3,5 +3,5 @@
|
|||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Views params={data}/>
|
<Views {...data}/>
|
||||||
|
|
||||||
|
|||||||
@@ -3,5 +3,5 @@
|
|||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Views params={data}/>
|
<Views {...data}/>
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export const actions = {
|
|||||||
const body_data = {
|
const body_data = {
|
||||||
workingDay: data.get('workingDay'),
|
workingDay: data.get('workingDay'),
|
||||||
workingTime: data.get('workingTime'),
|
workingTime: data.get('workingTime'),
|
||||||
|
isHomeOffice: data.get('isHomeOffice'),
|
||||||
}
|
}
|
||||||
await WorkingHoursRepository.update(env.API_URL, data.get('workingDay'), body_data);
|
await WorkingHoursRepository.update(env.API_URL, data.get('workingDay'), body_data);
|
||||||
},
|
},
|
||||||
@@ -42,6 +43,7 @@ export const actions = {
|
|||||||
const body_data = {
|
const body_data = {
|
||||||
workingDay: form_data.get('workingDay'),
|
workingDay: form_data.get('workingDay'),
|
||||||
workingTime: form_data.get('workingTime'),
|
workingTime: form_data.get('workingTime'),
|
||||||
|
isHomeOffice: form_data.get('isHomeOffice'),
|
||||||
}
|
}
|
||||||
await WorkingHoursRepository.add(env.API_URL, body_data);
|
await WorkingHoursRepository.add(env.API_URL, body_data);
|
||||||
},
|
},
|
||||||
@@ -56,13 +58,19 @@ export const actions = {
|
|||||||
actualDayObject.setDate(actualDayObject.getDate() + 1)
|
actualDayObject.setDate(actualDayObject.getDate() + 1)
|
||||||
})
|
})
|
||||||
for (const pair of form_data.entries()) {
|
for (const pair of form_data.entries()) {
|
||||||
const working_day = day_list[pair[0]] ?? null
|
let key = /(?<day>\w+)\[time]/.exec(pair[0])
|
||||||
if (working_day === null) {
|
if (key === null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let day = key?.groups?.day ?? ''
|
||||||
|
const working_day = day_list[day] ?? null
|
||||||
|
if (working_day === null || pair[1] === '00:00:00' || pair[1] === '00:00') {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const body_data = {
|
const body_data = {
|
||||||
workingDay: working_day,
|
workingDay: working_day,
|
||||||
workingTime: pair[1],
|
workingTime: pair[1],
|
||||||
|
isHomeOffice: form_data.has(day + '[isHomeOffice]')
|
||||||
}
|
}
|
||||||
await WorkingHoursRepository.addOrUpdate(env.API_URL, working_day, body_data);
|
await WorkingHoursRepository.addOrUpdate(env.API_URL, working_day, body_data);
|
||||||
}
|
}
|
||||||
@@ -70,10 +78,15 @@ export const actions = {
|
|||||||
'import-csv': async ({ request }) => {
|
'import-csv': async ({ request }) => {
|
||||||
const form_data = await request.formData();
|
const form_data = await request.formData();
|
||||||
for (const pair of form_data.entries()) {
|
for (const pair of form_data.entries()) {
|
||||||
const working_day = pair[0]
|
let key = /^(?<day>\d{4}-\d{2}-\d{2})\[time]$/.exec(pair[0])
|
||||||
|
if (key === null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let working_day = key?.groups?.day ?? ''
|
||||||
const body_data = {
|
const body_data = {
|
||||||
workingDay: working_day,
|
workingDay: working_day,
|
||||||
workingTime: pair[1],
|
workingTime: pair[1],
|
||||||
|
isHomeOffice: form_data.has(working_day + '[isHomeOffice]')
|
||||||
}
|
}
|
||||||
await WorkingHoursRepository.addOrUpdate(env.API_URL, working_day, body_data);
|
await WorkingHoursRepository.addOrUpdate(env.API_URL, working_day, body_data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,16 @@
|
|||||||
import documentIcon from "@iconify-icons/oi/document.js";
|
import documentIcon from "@iconify-icons/oi/document.js";
|
||||||
import wrenchIcon from "@iconify-icons/oi/wrench.js";
|
import wrenchIcon from "@iconify-icons/oi/wrench.js";
|
||||||
import IconifyIcon from "@iconify/svelte";
|
import IconifyIcon from "@iconify/svelte";
|
||||||
|
import TablePagination from "$lib/TablePagination.svelte";
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
const TITLE = "Bearbeitung Einträge"
|
const TITLE = "Bearbeitung Einträge"
|
||||||
/** @type {{workingDay: String, workingTime: String, }[]|null} **/
|
/** @type {{workingDay: String, workingTime: String, isHomeOffice: Boolean, }[]|null} **/
|
||||||
let records = $state([])
|
let records = $state(null)
|
||||||
|
/** @type {{workingDay: String, workingTime: String, isHomeOffice: Boolean, }[]} **/
|
||||||
|
let actual_records = $state([])
|
||||||
const init = async () => await data.promise.then(
|
const init = async () => await data.promise.then(
|
||||||
/**
|
/**
|
||||||
* @param {{workingDay: String, workingTime: String, }[]} data
|
* @param {{workingDay: String, workingTime: String, isHomeOffice: Boolean, }[]} data
|
||||||
*/
|
*/
|
||||||
data => {
|
data => {
|
||||||
records = data
|
records = data
|
||||||
@@ -53,14 +56,16 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Datum</th>
|
<th>Datum</th>
|
||||||
<th>Arbeitsstunden</th>
|
<th>Arbeitsstunden</th>
|
||||||
|
<th>Zu Hause gearbeitet?</th>
|
||||||
<th>Aktionen</th>
|
<th>Aktionen</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each records as record}
|
{#each actual_records as record}
|
||||||
<tr>
|
<tr style="{record.isHomeOffice ? 'background: aqua' : ''}">
|
||||||
<td>{record.workingDay}</td>
|
<td>{record.workingDay}</td>
|
||||||
<td>{record.workingTime}</td>
|
<td>{record.workingTime}</td>
|
||||||
|
<td>{record.isHomeOffice ? 'ja' : ''}</td>
|
||||||
<td>
|
<td>
|
||||||
<button formaction={'/working-hours/' + record.workingDay} type="submit">Bearbeiten
|
<button formaction={'/working-hours/' + record.workingDay} type="submit">Bearbeiten
|
||||||
<IconifyIcon icon={wrenchIcon}/>
|
<IconifyIcon icon={wrenchIcon}/>
|
||||||
@@ -69,6 +74,13 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">
|
||||||
|
<TablePagination promise_data={records} bind:actual_records={actual_records} page_count={15}/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
{/if}
|
{/if}
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
import xIcon from "@iconify-icons/oi/x";
|
import xIcon from "@iconify-icons/oi/x";
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
const TITLE = "Bearbeitung eines Eintrags"
|
const TITLE = "Bearbeitung eines Eintrags"
|
||||||
/** @type {{workingDay: String, workingTime: String, }|null} **/
|
/** @type {{workingDay: String, workingTime: String, isHomeOffice: Boolean, }|null} **/
|
||||||
let activeDate = $state(null)
|
let activeDate = $state(null)
|
||||||
const init = async () => await data.promise.then(
|
const init = async () => await data.promise.then(
|
||||||
/**
|
/**
|
||||||
* @param {{workingDay: String, workingTime: String, }} data
|
* @param {{workingDay: String, workingTime: String, isHomeOffice: Boolean, }} data
|
||||||
*/
|
*/
|
||||||
data => {
|
data => {
|
||||||
activeDate = data
|
activeDate = data
|
||||||
@@ -42,6 +42,10 @@
|
|||||||
value="{activeDate.workingTime}"
|
value="{activeDate.workingTime}"
|
||||||
id="workingTime" required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
id="workingTime" required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="isHomeOffice">Arbeitszeit</label>
|
||||||
|
<input name="isHomeOffice" type="checkbox" checked={activeDate.isHomeOffice} id="isHomeOffice">
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{/if}
|
{/if}
|
||||||
<fieldset name="buttons">
|
<fieldset name="buttons">
|
||||||
|
|||||||
@@ -29,6 +29,10 @@
|
|||||||
<input name="workingTime" placeholder="Arbeitszeit" type="time" step="1" value=""
|
<input name="workingTime" placeholder="Arbeitszeit" type="time" step="1" value=""
|
||||||
id="workingTime" required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
id="workingTime" required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="isHomeOffice">Arbeitszeit</label>
|
||||||
|
<input name="isHomeOffice" type="checkbox" id="isHomeOffice">
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset name="buttons">
|
<fieldset name="buttons">
|
||||||
<button type="submit">Übernehmen und Weiter
|
<button type="submit">Übernehmen und Weiter
|
||||||
|
|||||||
@@ -14,6 +14,22 @@
|
|||||||
tbody.weekend td input, tbody.weekend td {
|
tbody.weekend td input, tbody.weekend td {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
tbody.weekend label {
|
||||||
|
color: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
section form fieldset div input {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.25rem;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
section form fieldset div input[type=checkbox] {
|
||||||
|
display: inline-block;
|
||||||
|
width: auto;
|
||||||
|
margin: 0 1rem 1.0rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -39,10 +55,19 @@
|
|||||||
{day}
|
{day}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input name="{day}" value="08:00:00" type="time" step="1"
|
<input name="{day}[time]" value="08:00:00" type="time" step="1"
|
||||||
required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<label>wurde zu Hause gearbeitet?
|
||||||
|
<input name="{day}[isHomeOffice]" type="checkbox">
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
<tbody class="weekend">
|
<tbody class="weekend">
|
||||||
@@ -52,10 +77,19 @@
|
|||||||
{day}
|
{day}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input name="{day}" value="00:00:00" type="time" step="1"
|
<input name="{day}[time]" value="00:00:00" type="time" step="1"
|
||||||
required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<label>wurde zu Hause gearbeitet?
|
||||||
|
<input name="{day}[isHomeOffice]" type="checkbox">
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
const TITLE = "Erstellung neuer Einträge für eine Woche"
|
const TITLE = "Erstellung neuer Einträge für eine Woche"
|
||||||
|
|
||||||
/** @type {{ workingTime: String, workingDay: String, }[]} **/
|
/** @type {{ workingTime: String, workingDay: String, isHomeOffice: Boolean, }[]} **/
|
||||||
let imported_data = $state([])
|
let imported_data = $state([])
|
||||||
|
|
||||||
|
|
||||||
@@ -30,6 +30,8 @@
|
|||||||
complete: results => {
|
complete: results => {
|
||||||
/** @type {Object<String, Number>} **/
|
/** @type {Object<String, Number>} **/
|
||||||
let cash_work_days = {}
|
let cash_work_days = {}
|
||||||
|
/** @type {Object<String, Boolean>} **/
|
||||||
|
let home_office_days = {}
|
||||||
results.data.forEach(
|
results.data.forEach(
|
||||||
/** @param {Object<String, any>} item **/
|
/** @param {Object<String, any>} item **/
|
||||||
item => {
|
item => {
|
||||||
@@ -42,6 +44,9 @@
|
|||||||
let work_to = new Date(item['Bis'])
|
let work_to = new Date(item['Bis'])
|
||||||
let actual_day = work_from.toISOString().substring(0, 10)
|
let actual_day = work_from.toISOString().substring(0, 10)
|
||||||
let actual_work_hours = (work_to.getTime() - work_from.getTime()) / (1000 * 60 * 60)
|
let actual_work_hours = (work_to.getTime() - work_from.getTime()) / (1000 * 60 * 60)
|
||||||
|
if (activity.trim() === 'Verkehr') {
|
||||||
|
home_office_days[actual_day] = true
|
||||||
|
}
|
||||||
cash_work_days[actual_day] ??= 0
|
cash_work_days[actual_day] ??= 0
|
||||||
cash_work_days[actual_day] += actual_work_hours
|
cash_work_days[actual_day] += actual_work_hours
|
||||||
}
|
}
|
||||||
@@ -52,7 +57,11 @@
|
|||||||
let minutes_digits = Math.round((hours - hours_digits) * 60)
|
let minutes_digits = Math.round((hours - hours_digits) * 60)
|
||||||
let working_time = String(hours_digits).padStart(2, '0') + ':'
|
let working_time = String(hours_digits).padStart(2, '0') + ':'
|
||||||
+ String(minutes_digits).padStart(2, '0') + ':00'
|
+ String(minutes_digits).padStart(2, '0') + ':00'
|
||||||
imported_data.push({workingTime: working_time, workingDay: day})
|
imported_data.push({
|
||||||
|
workingTime: working_time,
|
||||||
|
workingDay: day,
|
||||||
|
isHomeOffice: home_office_days[day] ?? false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -90,7 +99,10 @@
|
|||||||
{item.workingDay}
|
{item.workingDay}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="time" readonly name="{item.workingDay}" value="{item.workingTime}">
|
<input type="time" readonly name="{item.workingDay}[time]" value="{item.workingTime}">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" readonly name="{item.workingDay}[isHomeOffice]" checked={item.isHomeOffice}>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
Reference in New Issue
Block a user