inital commit

This commit is contained in:
Tim Lappe 2025-03-11 20:36:03 +01:00
commit f3205fe53d
8 changed files with 1786 additions and 0 deletions

1
.env Normal file
View File

@ -0,0 +1 @@
APP_ENV=dev

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.env.local
.env.development.local
.env.test.local
.idea
vendor
var/

16
composer.json Normal file
View File

@ -0,0 +1,16 @@
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"require": {
"php": "^8.2",
"symfony/http-client": "^7.1",
"dflydev/hawk": "^0.0.0",
"guzzlehttp/guzzle": "^7.9",
"symfony/dotenv": "^7.1",
"symfony/cache": "^7.1",
"ext-intl": "*"
}
}

1504
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

19
config/nginx.conf Normal file
View File

@ -0,0 +1,19 @@
server {
listen 80;
root /home/tim/app/public;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
}

146
public/index.php Normal file
View File

@ -0,0 +1,146 @@
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Arbeitstage FTK - Tim Lappe</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.type-urlaub {
background-color: rgb(166, 199, 203);
}
.type-ftk {
background-color: rgb(0, 124, 0);
color: white;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.5);
z-index: 10;
position: relative;
}
</style>
</head>
<body>
<?php
if (!isset($_GET['key']) || $_GET['key'] !== "74857389798572903480209489024") {
echo "Kein Zugriff";
return;
}
ini_set('display_errors', 1);
use App\App;
require_once __DIR__ . '/../vendor/autoload.php';
Locale::setDefault('de_DE');
setlocale(LC_ALL, "de_DE"); //only necessary if the locale isn't already set
$formatter = new IntlDateFormatter(
'de_DE',
IntlDateFormatter::FULL,
IntlDateFormatter::FULL,
'Europe/Berlin',
IntlDateFormatter::GREGORIAN,
'EEEE'
);
$app = new App();
$app->init();
$data = ($app->getAbsenceClient())->getAbsences([
'skip' => 0,
'limit' => 50,
'filter' => [
'start' => ['$gte' => (new DateTime('-1 day'))->format('Y-m-d\TH:i:s.u\Z')],
'assignedTo:user._id' => [
'email' => 'tim.lappe@check24.de'
]
],
'sortBy' => [
'start' => 1
],
'relations' => ['assignedToId', 'reasonId', 'approverId']
]);
$data = $data['data'];
// Funktion zum Erstellen einer Liste aller Tage in einem bestimmten Zeitraum
function getAllDays($startDate, $endDate) {
$period = new DatePeriod(
new DateTime($startDate),
new DateInterval('P1D'),
(new DateTime($endDate))->modify('+1 day')
);
$days = [];
foreach ($period as $date) {
$days[] = $date->format('Y-m-d');
}
return $days;
}
// Beispiel: Zeitraum eines Monats (kann angepasst werden)
$startDate = (new DateTime())->format('Y-m-d');
$endDate = (new DateTime('+90 days'))->format('Y-m-d');
$allDays = getAllDays($startDate, $endDate);
?>
<div class="container mt-5">
<h1>Arbeitstage FTK - Tim Lappe</h1>
<p>Zeitraum: <?= $startDate; ?> - <?= $endDate; ?></p>
<table class="table table-borderless" style="table-layout: fixed">
<thead>
<tr>
<th style="width: 100px">Datum</th>
<th>Wochentag</th>
<th>Typ</th>
</tr>
</thead>
<tbody>
<?php foreach ($allDays as $day): ?>
<?php
$reason = '';
$isFtk = false;
$isUrlaub = false;
$class = 'bg-light text-dark';
// Prüfen, ob es für diesen Tag eine Abwesenheit gibt
foreach ($data as $absence) {
foreach ($absence['days'] as $absenceDay) {
if ((new DateTime($absenceDay['date']))->format('Y-m-d') === $day) {
$reason = $absence['reason']['name'];
$isFtk = str_contains($reason, 'mobile Arbeit');
$isUrlaub = str_contains($reason, 'Urlaub') || str_contains($reason, 'Sonderurlaub');
$class = $isFtk ? 'type-ftk' : '';
$class = $isUrlaub ? 'type-urlaub' : $class;
break 2;
}
}
}
?>
<?php if (DateTimeImmutable::createFromFormat('Y-m-d', $day)->format('N') == 6): ?>
<tr>
<td></td>
<td></td>
</tr>
<?php continue; ?>
<?php endif; ?>
<?php if (DateTimeImmutable::createFromFormat('Y-m-d', $day)->format('N') == 7): ?>
<tr class="bg-white text-secondary">
<td colspan="2">KW <?php echo ((int) (new DateTime($day))->format('W')) + 1; ?></td>
</tr>
<?php continue; ?>
<?php endif; ?>
<tr class="<?= $class ?>">
<td><?= (new DateTime($day))->format('d.m.Y'); ?></td>
<td><?= $formatter->format((new DateTime($day))) ?></td>
<td><?php if ($isFtk): ?>FTK<?php endif; ?> <?php if($isUrlaub): ?>Urlaub<?php endif; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</body>
</html>

70
src/AbsenceClient.php Normal file
View File

@ -0,0 +1,70 @@
<?php
namespace App;
use Dflydev\Hawk\Client\ClientBuilder;
use Dflydev\Hawk\Credentials\Credentials;
use GuzzleHttp\Client as Guzzle;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Request;
use Psr\Cache\InvalidArgumentException;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
class AbsenceClient
{
private Guzzle $client;
private FilesystemAdapter $cache;
public function __construct(private string $key, private string $id)
{
$this->client = new Guzzle();
$this->cache = new FilesystemAdapter(defaultLifetime: 3600, directory: __DIR__ . '/../var/cache');
}
private function getRequest(string $method, string $path, array $data): Request
{
$credentials = new Credentials($this->key, 'sha256', $this->id);
$client = ClientBuilder::create()->build();
$request = $client->createRequest(
$credentials,
$path,
$method,
['content-type' => 'application/json']
);
return new Request($method, $path, [
'Authorization' => $request->header()->fieldValue(),
'Content-Type' => 'application/json',
], json_encode($data));
}
/**
* @throws InvalidArgumentException
*/
public function getAbsences(array $filter): array
{
$request = $this->cache->get("get_absences", function ($item) use ($filter) {
echo "Cache miss\n";
$item->expiresAfter(3600);
$request = $this->getRequest('POST', 'https://app.absence.io/api/v2/absences', $filter);
try {
$res = $this->client->send($request);
} catch (GuzzleException $e) {
return [];
}
return json_decode($res->getBody()->getContents(), true);
});
$this->cache->commit();
return $request;
}
public function getClient(): Guzzle
{
return $this->client;
}
}

24
src/App.php Normal file
View File

@ -0,0 +1,24 @@
<?php
namespace App;
use Symfony\Component\Dotenv\Dotenv;
class App
{
private AbsenceClient $absenceClient;
public function init(): void
{
$dotenv = new Dotenv();
$dotenv->load(__DIR__ . '/../.env');
$dotenv->overload(__DIR__ . '/../.env.local');
$this->absenceClient = new AbsenceClient($_ENV['ABSENCE_KEY'], $_ENV['ABSENCE_ID']);
}
public function getAbsenceClient(): AbsenceClient
{
return $this->absenceClient;
}
}