mirror of
https://github.com/Karaka-Management/oms-HumanResourceTimeRecording.git
synced 2026-01-11 03:48:40 +00:00
more tests and some fixes
This commit is contained in:
parent
e76984148b
commit
c04df74e70
9
.github/workflows/main.yml
vendored
9
.github/workflows/main.yml
vendored
|
|
@ -13,10 +13,15 @@ on:
|
||||||
- cron: '0 0 1,15 * *'
|
- cron: '0 0 1,15 * *'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
general_module_workflow:
|
general_module_workflow_php:
|
||||||
uses: Karaka-Management/Karaka/.github/workflows/php_template.yml@develop
|
uses: Karaka-Management/Karaka/.github/workflows/php_template.yml@develop
|
||||||
secrets:
|
secrets:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
GH_PAT: ${{ secrets.GH_PAT }}
|
GH_PAT: ${{ secrets.GH_PAT }}
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
general_module_workflow_js:
|
||||||
|
uses: Karaka-Management/Karaka/.github/workflows/js_template.yml@develop
|
||||||
|
secrets:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GH_PAT: ${{ secrets.GH_PAT }}
|
||||||
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
@ -1,461 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Jingga
|
|
||||||
*
|
|
||||||
* PHP Version 8.2
|
|
||||||
*
|
|
||||||
* @package Web\Timerecording
|
|
||||||
* @copyright Dennis Eichhorn
|
|
||||||
* @license OMS License 2.0
|
|
||||||
* @version 1.0.0
|
|
||||||
* @link https://jingga.app
|
|
||||||
*/
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Web\Timerecording;
|
|
||||||
|
|
||||||
use Model\CoreSettings;
|
|
||||||
use Modules\Admin\Models\AccountMapper;
|
|
||||||
use Modules\Admin\Models\LocalizationMapper;
|
|
||||||
use Modules\Admin\Models\SettingsEnum;
|
|
||||||
use Modules\Organization\Models\UnitMapper;
|
|
||||||
use Modules\Profile\Models\ProfileMapper;
|
|
||||||
use phpOMS\Account\Account;
|
|
||||||
use phpOMS\Account\AccountManager;
|
|
||||||
use phpOMS\Account\PermissionType;
|
|
||||||
use phpOMS\Asset\AssetType;
|
|
||||||
use phpOMS\Auth\Auth;
|
|
||||||
use phpOMS\DataStorage\Cache\CachePool;
|
|
||||||
use phpOMS\DataStorage\Cookie\CookieJar;
|
|
||||||
use phpOMS\DataStorage\Database\Connection\ConnectionAbstract;
|
|
||||||
use phpOMS\DataStorage\Database\DatabasePool;
|
|
||||||
use phpOMS\DataStorage\Database\DatabaseStatus;
|
|
||||||
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;
|
|
||||||
use phpOMS\DataStorage\Session\HttpSession;
|
|
||||||
use phpOMS\Dispatcher\Dispatcher;
|
|
||||||
use phpOMS\Event\EventManager;
|
|
||||||
use phpOMS\Localization\L11nManager;
|
|
||||||
use phpOMS\Message\Http\HttpRequest;
|
|
||||||
use phpOMS\Message\Http\HttpResponse;
|
|
||||||
use phpOMS\Message\Http\RequestMethod;
|
|
||||||
use phpOMS\Message\Http\RequestStatusCode;
|
|
||||||
use phpOMS\Model\Html\Head;
|
|
||||||
use phpOMS\Module\ModuleManager;
|
|
||||||
use phpOMS\Router\RouteVerb;
|
|
||||||
use phpOMS\Router\WebRouter;
|
|
||||||
use phpOMS\System\File\PathException;
|
|
||||||
use phpOMS\Uri\UriFactory;
|
|
||||||
use phpOMS\Views\View;
|
|
||||||
use Web\WebApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Application class.
|
|
||||||
*
|
|
||||||
* @package Web\Timerecording
|
|
||||||
* @license OMS License 2.0
|
|
||||||
* @link https://jingga.app
|
|
||||||
* @since 1.0.0
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
final class Application
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* WebApplication.
|
|
||||||
*
|
|
||||||
* @var WebApplication
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private WebApplication $app;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Temp config.
|
|
||||||
*
|
|
||||||
* @var array{db:array{core:array{masters:array{select:array{db:string, host:string, port:int, login:string, password:string, database:string}}}}, log:array{file:array{path:string}}, app:array{path:string, default:string, domains:array}, page:array{root:string, https:bool}, language:string[]}
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private array $config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param WebApplication $app WebApplication
|
|
||||||
* @param array{db:array{core:array{masters:array{select:array{db:string, host:string, port:int, login:string, password:string, database:string}}}}, log:array{file:array{path:string}}, app:array{path:string, default:string, domains:array}, page:array{root:string, https:bool}, language:string[]} $config Application config
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public function __construct(WebApplication $app, array $config)
|
|
||||||
{
|
|
||||||
$this->app = $app;
|
|
||||||
$this->app->appName = 'Timerecording';
|
|
||||||
$this->config = $config;
|
|
||||||
UriFactory::setQuery('/app', \strtolower($this->app->appName));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rendering timerecording.
|
|
||||||
*
|
|
||||||
* @param HttpRequest $request Request
|
|
||||||
* @param HttpResponse $response Response
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public function run(HttpRequest $request, HttpResponse $response) : void
|
|
||||||
{
|
|
||||||
$this->app->l11nManager = new L11nManager();
|
|
||||||
|
|
||||||
$pageView = new TimerecordingView($this->app->l11nManager, $request, $response);
|
|
||||||
$head = new Head();
|
|
||||||
|
|
||||||
$pageView->head = $head;
|
|
||||||
$response->set('Content', $pageView);
|
|
||||||
|
|
||||||
/* Timerecording only allows GET */
|
|
||||||
if ($request->getMethod() !== RequestMethod::GET) {
|
|
||||||
$this->create406Response($response, $pageView);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->app->dbPool = new DatabasePool();
|
|
||||||
$this->app->router = new WebRouter();
|
|
||||||
$this->app->router->importFromFile(__DIR__ . '/Routes.php');
|
|
||||||
$this->app->router->add(
|
|
||||||
'/timerecording/e403',
|
|
||||||
function() use ($request, $response) {
|
|
||||||
$view = new View($this->app->l11nManager, $request, $response);
|
|
||||||
$view->setTemplate('/Web/Timerecording/Error/403_inline');
|
|
||||||
$response->header->status = RequestStatusCode::R_403;
|
|
||||||
|
|
||||||
return $view;
|
|
||||||
},
|
|
||||||
RouteVerb::GET
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->app->sessionManager = new HttpSession(60);
|
|
||||||
$this->app->cookieJar = new CookieJar();
|
|
||||||
$this->app->moduleManager = new ModuleManager($this->app, __DIR__ . '/../../Modules/');
|
|
||||||
$this->app->dispatcher = new Dispatcher($this->app);
|
|
||||||
|
|
||||||
$this->app->dbPool->create('select', $this->config['db']['core']['masters']['select']);
|
|
||||||
|
|
||||||
/* Database OK? */
|
|
||||||
if ($this->app->dbPool->get()->getStatus() !== DatabaseStatus::OK) {
|
|
||||||
$this->create503Response($response, $pageView);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CSRF token OK? */
|
|
||||||
if ($request->hasData('CSRF')
|
|
||||||
&& !\hash_equals($this->app->sessionManager->data['CSRF'] ?? '', $request->getDataString('CSRF'))
|
|
||||||
) {
|
|
||||||
$response->header->status = RequestStatusCode::R_403;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var ConnectionAbstract $con */
|
|
||||||
$con = $this->app->dbPool->get();
|
|
||||||
DataMapperFactory::db($con);
|
|
||||||
|
|
||||||
$this->app->cachePool = new CachePool();
|
|
||||||
$this->app->appSettings = new CoreSettings();
|
|
||||||
$this->app->eventManager = new EventManager($this->app->dispatcher);
|
|
||||||
$this->app->accountManager = new AccountManager($this->app->sessionManager);
|
|
||||||
$this->app->l11nServer = LocalizationMapper::get()->where('id', 1)->execute();
|
|
||||||
|
|
||||||
$this->app->unitId = $this->getApplicationOrganization($request, $this->config);
|
|
||||||
$pageView->setData('unitId', $this->app->unitId);
|
|
||||||
|
|
||||||
$aid = Auth::authenticate($this->app->sessionManager);
|
|
||||||
$request->header->account = $aid;
|
|
||||||
$response->header->account = $aid;
|
|
||||||
|
|
||||||
$account = $this->loadAccount($request);
|
|
||||||
|
|
||||||
if ($account->id > 0) {
|
|
||||||
$response->header->l11n = $account->l11n;
|
|
||||||
} elseif (isset($this->app->sessionManager->data['language'])
|
|
||||||
&& $response->header->l11n->language !== $this->app->sessionManager->data['language']
|
|
||||||
) {
|
|
||||||
$response->header->l11n
|
|
||||||
->loadFromLanguage(
|
|
||||||
$this->app->sessionManager->data['language'],
|
|
||||||
$this->app->sessionManager->data['country'] ?? '*'
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->app->setResponseLanguage($request, $response, $this->config);
|
|
||||||
}
|
|
||||||
|
|
||||||
UriFactory::setQuery('/lang', $response->header->l11n->language);
|
|
||||||
|
|
||||||
$this->loadLanguageFromPath(
|
|
||||||
$response->header->l11n->language,
|
|
||||||
__DIR__ . '/lang/' . $response->header->l11n->language . '.lang.php'
|
|
||||||
);
|
|
||||||
|
|
||||||
$response->header->set('content-language', $response->header->l11n->language, true);
|
|
||||||
|
|
||||||
/* Create html head */
|
|
||||||
$this->initResponseHead($head, $request, $response);
|
|
||||||
|
|
||||||
/* Handle not logged in */
|
|
||||||
if ($account->id < 1) {
|
|
||||||
$this->createLoggedOutResponse($response, $head, $pageView);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No reading permission */
|
|
||||||
if (!$account->hasPermission(PermissionType::READ, $this->app->unitId, $this->app->appId, 'Dashboard')) {
|
|
||||||
$this->create403Response($response, $pageView);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->app->moduleManager->initRequestModules($request);
|
|
||||||
$this->createDefaultPageView($request, $response, $pageView);
|
|
||||||
|
|
||||||
$dispatched = $this->app->dispatcher->dispatch(
|
|
||||||
$this->app->router->route(
|
|
||||||
$request->uri->getRoute(),
|
|
||||||
$request->getDataString('CSRF'),
|
|
||||||
$request->getRouteVerb(),
|
|
||||||
$this->app->appId,
|
|
||||||
$this->app->unitId,
|
|
||||||
$account,
|
|
||||||
$request->getData()
|
|
||||||
),
|
|
||||||
$request,
|
|
||||||
$response
|
|
||||||
);
|
|
||||||
$pageView->addData('dispatch', $dispatched);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get application organization
|
|
||||||
*
|
|
||||||
* @param HttpRequest $request Client request
|
|
||||||
* @param array $config App config
|
|
||||||
*
|
|
||||||
* @return int Organization id
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private function getApplicationOrganization(HttpRequest $request, array $config) : int
|
|
||||||
{
|
|
||||||
return (int) (
|
|
||||||
$request->getData('u') ?? (
|
|
||||||
$config['domains'][$request->uri->host]['org'] ?? $this->app->appSettings->get(
|
|
||||||
SettingsEnum::DEFAULT_UNIT
|
|
||||||
) ?? 1
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create 406 response.
|
|
||||||
*
|
|
||||||
* @param HttpResponse $response Response
|
|
||||||
* @param View $pageView View
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private function create406Response(HttpResponse $response, View $pageView) : void
|
|
||||||
{
|
|
||||||
$response->header->status = RequestStatusCode::R_406;
|
|
||||||
$pageView->setTemplate('/Web/Timerecording/Error/406');
|
|
||||||
$this->loadLanguageFromPath(
|
|
||||||
$response->header->l11n->language,
|
|
||||||
__DIR__ . '/Error/lang/' . $response->header->l11n->language . '.lang.php'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create 406 response.
|
|
||||||
*
|
|
||||||
* @param HttpResponse $response Response
|
|
||||||
* @param View $pageView View
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private function create503Response(HttpResponse $response, View $pageView) : void
|
|
||||||
{
|
|
||||||
$response->header->status = RequestStatusCode::R_503;
|
|
||||||
$pageView->setTemplate('/Web/Timerecording/Error/503');
|
|
||||||
$this->loadLanguageFromPath(
|
|
||||||
$response->header->l11n->language,
|
|
||||||
__DIR__ . '/Error/lang/' . $response->header->l11n->language . '.lang.php'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load theme language from path
|
|
||||||
*
|
|
||||||
* @param string $language Language name
|
|
||||||
* @param string $path Language path
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @throws PathException
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private function loadLanguageFromPath(string $language, string $path) : void
|
|
||||||
{
|
|
||||||
/* Load theme language */
|
|
||||||
if (($absPath = \realpath($path)) === false) {
|
|
||||||
throw new PathException($path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection PhpIncludeInspection */
|
|
||||||
$themeLanguage = include $absPath;
|
|
||||||
$this->app->l11nManager->loadLanguage($language, '0', $themeLanguage);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load permission
|
|
||||||
*
|
|
||||||
* @param HttpRequest $request Current request
|
|
||||||
*
|
|
||||||
* @return Account
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private function loadAccount(HttpRequest $request) : Account
|
|
||||||
{
|
|
||||||
$account = AccountMapper::getWithPermissions($request->header->account);
|
|
||||||
$this->app->accountManager->add($account);
|
|
||||||
|
|
||||||
return $account;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create 406 response.
|
|
||||||
*
|
|
||||||
* @param HttpResponse $response Response
|
|
||||||
* @param View $pageView View
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private function create403Response(HttpResponse $response, View $pageView) : void
|
|
||||||
{
|
|
||||||
$response->header->status = RequestStatusCode::R_403;
|
|
||||||
$pageView->setTemplate('/Web/Timerecording/Error/403');
|
|
||||||
$this->loadLanguageFromPath(
|
|
||||||
$response->header->l11n->language,
|
|
||||||
__DIR__ . '/Error/lang/' . $response->header->l11n->language . '.lang.php'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize response head
|
|
||||||
*
|
|
||||||
* @param Head $head Head to fill
|
|
||||||
* @param HttpRequest $request Request
|
|
||||||
* @param HttpResponse $response Response
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private function initResponseHead(Head $head, HttpRequest $request, HttpResponse $response) : void
|
|
||||||
{
|
|
||||||
/* Load assets */
|
|
||||||
$head->addAsset(AssetType::CSS, 'Resources/fonts/googleicons/styles.css', ['defer']);
|
|
||||||
$head->addAsset(AssetType::CSS, 'cssOMS/styles.css?v=1.0.0', ['defer']);
|
|
||||||
$head->addAsset(AssetType::CSS, 'Web/{APPNAME}/css/frontend.css?v=1.0.0', ['defer']);
|
|
||||||
|
|
||||||
// Framework
|
|
||||||
$head->addAsset(AssetType::JS, 'jsOMS/UnhandledException.js?v=1.0.0');
|
|
||||||
$head->addAsset(AssetType::JS, 'Web/Timerecording/js/timerecording.js?v=1.0.0', ['type' => 'module']);
|
|
||||||
$head->addAsset(AssetType::JSLATE, 'Modules/Navigation/Controller.js?v=1.0.0', ['type' => 'module']);
|
|
||||||
|
|
||||||
$script = '';
|
|
||||||
$response->header->set(
|
|
||||||
'content-security-policy',
|
|
||||||
'base-uri \'self\'; script-src \'self\' blob: \'sha256-'
|
|
||||||
. \base64_encode(\hash('sha256', $script, true))
|
|
||||||
. '\'; worker-src \'self\'',
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($request->hasData('debug')) {
|
|
||||||
$head->addAsset(AssetType::CSS, 'cssOMS/debug.css?v=1.0.0');
|
|
||||||
}
|
|
||||||
|
|
||||||
$css = \file_get_contents(__DIR__ . '/css/timerecording-small.css');
|
|
||||||
if ($css === false) {
|
|
||||||
$css = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$css = \preg_replace('!\s+!', ' ', $css);
|
|
||||||
$head->setStyle('core', $css ?? '');
|
|
||||||
$head->title = 'Karaka Timerecording';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create logged out response
|
|
||||||
*
|
|
||||||
* @param HttpResponse $response Response
|
|
||||||
* @param Head $head Head to fill
|
|
||||||
* @param View $pageView View
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private function createLoggedOutResponse(HttpResponse $response, Head $head, View $pageView) : void
|
|
||||||
{
|
|
||||||
$response->header->status = RequestStatusCode::R_403;
|
|
||||||
$pageView->setTemplate('/Web/Timerecording/login');
|
|
||||||
$head->addAsset(AssetType::JS, 'Web/Timerecording/js/login.js', ['type' => 'module']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create default page view
|
|
||||||
*
|
|
||||||
* @param HttpRequest $request Request
|
|
||||||
* @param HttpResponse $response Response
|
|
||||||
* @param TimerecordingView $pageView View
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private function createDefaultPageView(HttpRequest $request, HttpResponse $response, TimerecordingView $pageView) : void
|
|
||||||
{
|
|
||||||
$pageView->setOrganizations(UnitMapper::getAll()->execute());
|
|
||||||
$pageView->profile = ProfileMapper::get()->where('account', $request->header->account)->executeGetArray();
|
|
||||||
$pageView->setData('nav', $this->getNavigation($request, $response));
|
|
||||||
|
|
||||||
$pageView->setTemplate('/Web/Timerecording/index');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create navigation
|
|
||||||
*
|
|
||||||
* @param HttpRequest $request Request
|
|
||||||
* @param HttpResponse $response Response
|
|
||||||
*
|
|
||||||
* @return View
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
private function getNavigation(HttpRequest $request, HttpResponse $response) : View
|
|
||||||
{
|
|
||||||
/** @var \Modules\Navigation\Controller\TimerecordingController $navController */
|
|
||||||
$navController = $this->app->moduleManager->get('Navigation');
|
|
||||||
$navController->loadLanguage($request, $response);
|
|
||||||
|
|
||||||
return $navController->getView($request, $response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Inline
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
<?php
|
|
||||||
declare(strict_types=1);
|
|
||||||
return [
|
|
||||||
'^/timerecording$' => [
|
|
||||||
0 => [
|
|
||||||
'dest' => '\Modules\HumanResourceTimeRecording\Controller\TimerecordingController:viewDashboard',
|
|
||||||
'verb' => 1,
|
|
||||||
'active' => true,
|
|
||||||
'permission' => [
|
|
||||||
'module' => 'HumanResourceTimeRecording',
|
|
||||||
'type' => 2,
|
|
||||||
'state' => 1,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'^.*/timerecording/dashboard(\?.*$|$)' => [
|
|
||||||
0 => [
|
|
||||||
'dest' => '\Modules\HumanResourceTimeRecording\Controller\TimerecordingController:viewDashboard',
|
|
||||||
'verb' => 1,
|
|
||||||
'active' => true,
|
|
||||||
'permission' => [
|
|
||||||
'module' => 'HumanResourceTimeRecording',
|
|
||||||
'type' => 2,
|
|
||||||
'state' => 1,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
@ -1,190 +0,0 @@
|
||||||
:root {
|
|
||||||
--main-bg: rgb(46, 26, 90);
|
|
||||||
--main-bg-hl: rgb(158, 81, 197);
|
|
||||||
--iborder: rgba(166, 135, 232, .4);
|
|
||||||
--iborder-active: rgba(166, 135, 232, .7);
|
|
||||||
--ipt-c: rgb(116, 67, 161);
|
|
||||||
--ipt-c-active: rgb(94, 52, 133);
|
|
||||||
--ipt-bg: rgba(255, 255, 255);
|
|
||||||
--ipt-bg-active: rgba(255, 255, 255);
|
|
||||||
--ipt-ico-c: rgba(166, 135, 232, .6);
|
|
||||||
--ipt-ico-c-active: rgba(166, 135, 232, 1);
|
|
||||||
--btn-main-bg: rgba(166, 135, 232, .6);
|
|
||||||
--btn-main-bg-active: rgba(166, 135, 232, .8);
|
|
||||||
--btn-main-c: rgba(255, 255, 255, .9);
|
|
||||||
--txt-on-bg-c: rgba(255, 255, 255, 0.7);
|
|
||||||
--txt-on-bg-c-2: rgba(255, 255, 255, 0.85);
|
|
||||||
--nav-cat-bg: rgb(43, 58, 101);
|
|
||||||
--nav-cat-bg-hl: rgb(113, 43, 145);
|
|
||||||
--nav-cat-bg-hover: rgb(120, 50, 153);
|
|
||||||
--nav-sub-bg: rgb(72, 39, 102);
|
|
||||||
--nav-sub-bg-hl: rgb(94, 52, 133);
|
|
||||||
--nav-sub-bg-hover: rgb(116, 67, 161);
|
|
||||||
--nav-head-bg: rgb(72, 39, 102);
|
|
||||||
--nav-head-bg-hl: rgb(94, 52, 133);
|
|
||||||
--nav-head-bg-hover: rgb(116, 67, 161);
|
|
||||||
--nav-content-hover: rgb(177, 97, 218);
|
|
||||||
--ff: 'Arial', Helvetica, sans-serif;
|
|
||||||
--btn-bg: rgb(158, 81, 197);
|
|
||||||
--btn-bg-hover: rgb(177, 97, 218);
|
|
||||||
--tcaption-bg: rgb(255, 255, 255);
|
|
||||||
--thead-bg: rgb(236, 232, 255);
|
|
||||||
--trow-bg: rgb(255, 255, 255);
|
|
||||||
--trow-bg-alt: rgb(255, 255, 255);
|
|
||||||
--trow-bg-hover: rgb(220, 211, 255);
|
|
||||||
--link-c: rgb(72, 39, 102);
|
|
||||||
--lhover: rgb(158, 81, 197);
|
|
||||||
--badge-size: .55rem;
|
|
||||||
--badge-c: rgb(255, 255, 255);
|
|
||||||
--badge-bg: rgb(158, 81, 197);
|
|
||||||
--bborder: rgb(218, 218, 218); }
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
min-width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
font-family: var(--ff);
|
|
||||||
color: #000; }
|
|
||||||
|
|
||||||
body {
|
|
||||||
display: flex; flex-direction: column;}
|
|
||||||
|
|
||||||
header {
|
|
||||||
background: #f8f8f8;
|
|
||||||
border-bottom: 1px solid var(--bborder);
|
|
||||||
padding: 1rem;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-flow: row;
|
|
||||||
flex: 0; }
|
|
||||||
header > form {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
padding: 0 5px 0 5px;
|
|
||||||
max-width: 800px; }
|
|
||||||
header .inputWrapper {
|
|
||||||
flex: 1; }
|
|
||||||
header input[type=text] {
|
|
||||||
width: 100%;
|
|
||||||
background: white;
|
|
||||||
border: 1px solid var(--iborder);
|
|
||||||
text-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
transition: border 500ms ease-out;
|
|
||||||
outline: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding-left: 2rem; }
|
|
||||||
|
|
||||||
#logo {
|
|
||||||
flex: 1;
|
|
||||||
text-align: right; }
|
|
||||||
#logo select {
|
|
||||||
background: none;
|
|
||||||
color: rgba(255, 255, 255, 0.8);
|
|
||||||
font-size: .8rem; }
|
|
||||||
|
|
||||||
.ham-trigger {
|
|
||||||
display: flex;
|
|
||||||
color: #000;
|
|
||||||
align-items: center;
|
|
||||||
flex: 0;
|
|
||||||
margin-right: 5px; }
|
|
||||||
.ham-trigger i {
|
|
||||||
font-size: 1.5rem; }
|
|
||||||
|
|
||||||
nav .ham-trigger {
|
|
||||||
color: var(--txt-on-bg-c-2);
|
|
||||||
margin: 0 0 0 5px;
|
|
||||||
display: none; }
|
|
||||||
|
|
||||||
#t-nav-container {
|
|
||||||
margin-left: auto; }
|
|
||||||
|
|
||||||
#content {
|
|
||||||
flex: 1;
|
|
||||||
padding-left: 1rem;
|
|
||||||
overflow-y: auto; }
|
|
||||||
|
|
||||||
#t-nav {
|
|
||||||
font-size: .8rem;
|
|
||||||
color: #000;
|
|
||||||
font-weight: bold; }
|
|
||||||
#t-nav a {
|
|
||||||
padding: 0 5px 0 5px;
|
|
||||||
line-height: 25px; }
|
|
||||||
#t-nav i {
|
|
||||||
margin-right: 5px; }
|
|
||||||
#t-nav li {
|
|
||||||
display: inline; }
|
|
||||||
#t-nav li:hover {
|
|
||||||
color: var(--lhover); }
|
|
||||||
|
|
||||||
main {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
background: #f1f1f1;
|
|
||||||
flex: 1;
|
|
||||||
box-sizing: border-box; }
|
|
||||||
|
|
||||||
#dim {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: #000;
|
|
||||||
opacity: 0.5;
|
|
||||||
z-index: 5; }
|
|
||||||
|
|
||||||
#u-box {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 1rem 0 1rem;
|
|
||||||
height: 60px;
|
|
||||||
border-bottom: 1px solid var(--iborder);
|
|
||||||
box-sizing: border-box; }
|
|
||||||
#u-box img {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 50%; }
|
|
||||||
#u-box a {
|
|
||||||
display: inline-block; }
|
|
||||||
|
|
||||||
#app-message-container {
|
|
||||||
position: absolute;
|
|
||||||
margin: 0 auto;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
padding: 85px 10px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-msg {
|
|
||||||
z-index: 11;
|
|
||||||
position: relative;
|
|
||||||
margin: 0 auto;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 500px) {
|
|
||||||
nav .ham-trigger {
|
|
||||||
display: flex; } }
|
|
||||||
@media only screen and (max-width: 600px) {
|
|
||||||
header {
|
|
||||||
flex-flow: column;
|
|
||||||
height: auto;
|
|
||||||
padding: 1rem; }
|
|
||||||
header form {
|
|
||||||
width: 100%; }
|
|
||||||
|
|
||||||
#t-nav-container {
|
|
||||||
order: -1;
|
|
||||||
margin-bottom: .5rem; } }
|
|
||||||
@media only screen and (max-width: 1000px) {
|
|
||||||
#t-nav .link {
|
|
||||||
display: none; } }
|
|
||||||
|
|
@ -1,153 +0,0 @@
|
||||||
@import "backend_vars";
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
min-width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
font-family: var(--ff);
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
background: rgb(248, 248, 248);
|
|
||||||
border-bottom: 1px solid var(--bborder);
|
|
||||||
padding: 1rem;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-flow: row;
|
|
||||||
flex: 0;
|
|
||||||
|
|
||||||
> form {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
padding: 0 5px 0 5px;
|
|
||||||
max-width: 800px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputWrapper {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=text] {
|
|
||||||
width: 100%;
|
|
||||||
background: rgba(255, 255, 255, 1);
|
|
||||||
border: 1px solid var(--iborder);
|
|
||||||
text-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
transition : border 500ms ease-out;
|
|
||||||
outline: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding-left: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#logo {
|
|
||||||
flex: 1;
|
|
||||||
text-align: right;
|
|
||||||
|
|
||||||
select {
|
|
||||||
background: none;
|
|
||||||
color: rgba(255, 255, 255, 0.8);
|
|
||||||
font-size: .8rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#t-nav-container {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
flex: 1;
|
|
||||||
padding-left: 1rem;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#t-nav {
|
|
||||||
font-size: .8rem;
|
|
||||||
color: #000;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
a {
|
|
||||||
padding: 0 5px 0 5px;
|
|
||||||
line-height: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: inline;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--lhover);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
background: #f1f1f1;
|
|
||||||
|
|
||||||
flex: 1;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dim {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: #000;
|
|
||||||
opacity: 0.5;
|
|
||||||
z-index: 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#u-box {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 1rem 0 1rem;
|
|
||||||
height: 60px;
|
|
||||||
border-bottom: 1px solid var(--iborder);
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#app-message-container {
|
|
||||||
position: absolute;
|
|
||||||
margin: 0 auto;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
padding: 85px 10px 0 0;
|
|
||||||
|
|
||||||
.log-msg {
|
|
||||||
z-index: 11;
|
|
||||||
position: relative;
|
|
||||||
margin: 0 auto;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@import "timerecording_media";
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
@media only screen and (min-width: 1101px) {
|
|
||||||
.b-1 {
|
|
||||||
width: 31.5%;
|
|
||||||
width: calc(33.3% - 10px); }
|
|
||||||
|
|
||||||
.b-2 {
|
|
||||||
width: 48.5%;
|
|
||||||
width: calc(50% - 10px); }
|
|
||||||
|
|
||||||
.b-3 {
|
|
||||||
width: 63%;
|
|
||||||
width: calc(66.6% - 10px); } }
|
|
||||||
@media only screen and (min-width: 801px) and (max-width: 1100px) {
|
|
||||||
.b-1 {
|
|
||||||
width: 48.5%;
|
|
||||||
width: calc(50% - 10px); } }
|
|
||||||
@media only screen and (max-width: 1100px) {
|
|
||||||
.b-2, .b-3 {
|
|
||||||
width: 100%;
|
|
||||||
width: calc(100% - 10px); } }
|
|
||||||
@media only screen and (min-width: 801px) {
|
|
||||||
.b-4 {
|
|
||||||
width: 48.5%;
|
|
||||||
width: calc(50% - 10px); } }
|
|
||||||
@media only screen and (max-width: 800px) {
|
|
||||||
.b-1, .b-3 {
|
|
||||||
width: 100%;
|
|
||||||
width: calc(100% - 10px); }
|
|
||||||
|
|
||||||
#logo {
|
|
||||||
display: none; }
|
|
||||||
|
|
||||||
#s-nav {
|
|
||||||
width: 100%; }
|
|
||||||
|
|
||||||
#s-bar input[type=text] {
|
|
||||||
margin-left: 35px;
|
|
||||||
width: 80%;
|
|
||||||
width: calc(100% - 90px); } }
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
@media only screen and (max-width: 600px) {
|
|
||||||
header {
|
|
||||||
flex-flow: column;
|
|
||||||
height: auto;
|
|
||||||
padding: 1rem;
|
|
||||||
|
|
||||||
form {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
order: -1;
|
|
||||||
margin-bottom: .5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 1000px) {
|
|
||||||
nav .link {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
:root {
|
|
||||||
--main-bg: rgb(46, 26, 90);
|
|
||||||
--main-bg-hl: rgb(158, 81, 197);
|
|
||||||
--iborder: rgba(166, 135, 232, .4);
|
|
||||||
--iborder-active: rgba(166, 135, 232, .7);
|
|
||||||
--ipt-c: rgba(166, 135, 232, .6);
|
|
||||||
--ipt-c-active: rgba(166, 135, 232, .8);
|
|
||||||
--ipt-ico-c: rgba(166, 135, 232, .6);
|
|
||||||
--ipt-ico-c-active: rgba(166, 135, 232, 1);
|
|
||||||
--btn-main-bg: rgba(166, 135, 232, .6);
|
|
||||||
--btn-main-bg-active: rgba(166, 135, 232, .8);
|
|
||||||
--btn-main-c: rgba(255, 255, 255, .9);
|
|
||||||
--txt-on-bg-c: rgba(255, 255, 255, 0.7);
|
|
||||||
--nav-cat-bg: rgb(43, 58, 101);
|
|
||||||
--nav-cat-bg-hl: rgb(113, 43, 145);
|
|
||||||
--nav-cat-bg-hover: rgb(120, 50, 153);
|
|
||||||
--nav-sub-bg: rgb(72, 39, 102);
|
|
||||||
--nav-sub-bg-hl: rgb(94, 52, 133);
|
|
||||||
--nav-sub-bg-hover: rgb(116, 67, 161);
|
|
||||||
--nav-head-bg: rgb(72, 39, 102);
|
|
||||||
--nav-head-bg-hl: rgb(94, 52, 133);
|
|
||||||
--nav-head-bg-hover: rgb(116, 67, 161);
|
|
||||||
--ff: 'Arial', Helvetica, sans-serif;
|
|
||||||
--bborder-color: rgb(202, 202, 202); }
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
:root {
|
|
||||||
--main-bg: rgb(46, 26, 90);
|
|
||||||
--main-bg-hl: rgb(158, 81, 197);
|
|
||||||
|
|
||||||
--iborder: rgba(166, 135, 232, .4);
|
|
||||||
--iborder-active: rgba(166, 135, 232, .7);
|
|
||||||
--ipt-c: rgb(116, 67, 161);
|
|
||||||
--ipt-c-active: rgb(94, 52, 133);
|
|
||||||
--ipt-bg: rgba(255, 255, 255);
|
|
||||||
--ipt-bg-active: rgba(255, 255, 255);
|
|
||||||
|
|
||||||
--ipt-ico-c: rgba(166, 135, 232, .6);
|
|
||||||
--ipt-ico-c-active: rgba(166, 135, 232, 1);
|
|
||||||
|
|
||||||
--btn-main-bg: rgba(166, 135, 232, .6);
|
|
||||||
--btn-main-bg-active: rgba(166, 135, 232, .8);
|
|
||||||
--btn-main-c: rgba(255, 255, 255, .9);
|
|
||||||
|
|
||||||
--txt-on-bg-c: rgba(255, 255, 255, 0.7);
|
|
||||||
--txt-on-bg-c-2: rgba(255, 255, 255, 0.85);
|
|
||||||
|
|
||||||
--nav-cat-bg: rgb(43, 58, 101);
|
|
||||||
--nav-cat-bg-hl: rgb(113, 43, 145);
|
|
||||||
--nav-cat-bg-hover: rgb(120, 50, 153);
|
|
||||||
|
|
||||||
--nav-sub-bg: rgb(72, 39, 102);
|
|
||||||
--nav-sub-bg-hl: rgb(94, 52, 133);
|
|
||||||
--nav-sub-bg-hover: rgb(116, 67, 161);
|
|
||||||
|
|
||||||
--nav-head-bg: rgb(72, 39, 102);
|
|
||||||
--nav-head-bg-hl: rgb(94, 52, 133);
|
|
||||||
--nav-head-bg-hover: rgb(116, 67, 161);
|
|
||||||
|
|
||||||
--nav-content-hover: rgb(177, 97, 218);
|
|
||||||
|
|
||||||
--ff: 'Arial', Helvetica, sans-serif;
|
|
||||||
|
|
||||||
--btn-bg: rgb(158, 81, 197);
|
|
||||||
--btn-bg-hover: rgb(177, 97, 218);
|
|
||||||
|
|
||||||
--tcaption-bg: rgb(255, 255, 255);
|
|
||||||
--thead-bg: rgb(236, 232, 255);
|
|
||||||
--trow-bg: rgb(255, 255, 255);
|
|
||||||
--trow-bg-alt: rgb(255, 255, 255);
|
|
||||||
--trow-bg-hover: rgb(220, 211, 255);
|
|
||||||
|
|
||||||
--link-c: rgb(72, 39, 102);
|
|
||||||
--lhover: rgb(158, 81, 197);
|
|
||||||
|
|
||||||
--badge-size: .55rem;
|
|
||||||
--badge-c: rgb(255, 255, 255);
|
|
||||||
--badge-bg: rgb(158, 81, 197);
|
|
||||||
|
|
||||||
--bborder: rgb(218, 218, 218);
|
|
||||||
}
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Jingga
|
|
||||||
*
|
|
||||||
* PHP Version 8.2
|
|
||||||
*
|
|
||||||
* @package Web\Timerecording
|
|
||||||
* @copyright Dennis Eichhorn
|
|
||||||
* @license OMS License 2.0
|
|
||||||
* @version 1.0.0
|
|
||||||
* @link https://jingga.app
|
|
||||||
*/
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Web\Timerecording;
|
|
||||||
|
|
||||||
use Modules\Organization\Models\Unit;
|
|
||||||
use Modules\Profile\Models\Profile;
|
|
||||||
use phpOMS\Uri\UriFactory;
|
|
||||||
use phpOMS\Views\View;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main view.
|
|
||||||
*
|
|
||||||
* @package Web\Timerecording
|
|
||||||
* @license OMS License 2.0
|
|
||||||
* @link https://jingga.app
|
|
||||||
* @since 1.0.0
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
class TimerecordingView extends View
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Navigation view
|
|
||||||
*
|
|
||||||
* @var View
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
protected $nav = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User profile.
|
|
||||||
*
|
|
||||||
* @var Profile
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public $profile = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Organizations.
|
|
||||||
*
|
|
||||||
* @var Unit[]
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
protected $organizations = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set navigation view.
|
|
||||||
*
|
|
||||||
* @param View $nav Navigation view
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
public function setNavigation(View $nav) : void
|
|
||||||
{
|
|
||||||
$this->nav = $nav;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get profile image
|
|
||||||
*
|
|
||||||
* @return string Profile image link
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public function getProfileImage() : string
|
|
||||||
{
|
|
||||||
if ($this->profile === null || $this->profile->image->getPath() === '') {
|
|
||||||
return UriFactory::build('Web/Timerecording/img/user_default_' . \mt_rand(1, 6) . '.png');
|
|
||||||
}
|
|
||||||
|
|
||||||
return UriFactory::build($this->profile->image->getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set organizations
|
|
||||||
*
|
|
||||||
* @param Unit[] $organizations Organizations
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @since 1.0.0
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
public function setOrganizations(array $organizations) : void
|
|
||||||
{
|
|
||||||
$this->organizations = $organizations;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 361 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 86 KiB |
|
|
@ -1,69 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Jingga
|
|
||||||
*
|
|
||||||
* PHP Version 8.2
|
|
||||||
*
|
|
||||||
* @package Web\Timerecording
|
|
||||||
* @copyright Dennis Eichhorn
|
|
||||||
* @license OMS License 2.0
|
|
||||||
* @version 1.0.0
|
|
||||||
* @link https://jingga.app
|
|
||||||
*/
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
$nav = $this->getData('nav');
|
|
||||||
|
|
||||||
$nav->setTemplate('/Modules/Navigation/Theme/Backend/top');
|
|
||||||
$top = $nav->render();
|
|
||||||
|
|
||||||
/** @var phpOMS\Model\Html\Head $head */
|
|
||||||
$head = $this->head;
|
|
||||||
|
|
||||||
/** @var array $dispatch */
|
|
||||||
$dispatch = $this->getData('dispatch') ?? [];
|
|
||||||
?>
|
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html lang="<?= $this->printHtml($this->response->header->l11n->language); ?>">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<meta name="theme-color" content="#712b91">
|
|
||||||
<meta name="msapplication-navbutton-color" content="#712b91">
|
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="#712b91">
|
|
||||||
<meta name="description" content="<?= $this->getHtml(':meta', '0', '0'); ?>">
|
|
||||||
<?= $head->meta->render(); ?>
|
|
||||||
|
|
||||||
<base href="<?= \phpOMS\Uri\UriFactory::build('{/base}'); ?>/">
|
|
||||||
|
|
||||||
<link rel="manifest" href="<?= \phpOMS\Uri\UriFactory::build('Web/Timerecording/manifest.json'); ?>">
|
|
||||||
<link rel="shortcut icon" href="<?= \phpOMS\Uri\UriFactory::build('Web/Timerecording/img/favicon.ico'); ?>" type="image/x-icon">
|
|
||||||
|
|
||||||
<title><?= $this->printHtml($head->title); ?></title>
|
|
||||||
|
|
||||||
<?= $head->renderAssets(); ?>
|
|
||||||
|
|
||||||
<style><?= $head->renderStyle(); ?></style>
|
|
||||||
<script><?= $head->renderScript(); ?></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="vh" id="dim"></div>
|
|
||||||
<header><div id="t-nav-container"><?= $top; ?></div></header>
|
|
||||||
<main id="content" class="container-fluid" role="main">
|
|
||||||
<?php
|
|
||||||
foreach ($dispatch as $view) {
|
|
||||||
if ($view instanceof \phpOMS\Contract\RenderableInterface) {
|
|
||||||
echo $view->render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</main>
|
|
||||||
<div id="app-message-container">
|
|
||||||
<template id="app-message-tpl">
|
|
||||||
<div class="log-msg">
|
|
||||||
<h1 class="log-msg-title"></h1>
|
|
||||||
<div class="log-msg-content"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<?= $head->renderAssetsLate(); ?>
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import { redirectMessage } from '../../../../jsOMS/Model/Action/Dom/Redirect.js';
|
|
||||||
|
|
||||||
export const ACTION_EVENTS = {
|
|
||||||
'redirect': redirectMessage, /** global: redirectMessage */
|
|
||||||
};
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
/** global: jsOMS */
|
|
||||||
export const KEYBOARD_EVENTS = [
|
|
||||||
];
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
/** global: jsOMS */
|
|
||||||
export const MOUSE_EVENTS = [
|
|
||||||
];
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import { notifyMessage } from '../../../../jsOMS/Model/Message/Notify.js';
|
|
||||||
import { formValidationMessage } from '../../../../jsOMS/Model/Message/FormValidation.js';
|
|
||||||
import { redirectMessage } from '../../../../jsOMS/Model/Action/Dom/Redirect.js';
|
|
||||||
import { reloadButtonAction } from '../../../../jsOMS/Model/Action/Dom/Reload.js';
|
|
||||||
|
|
||||||
/** global: jsOMS */
|
|
||||||
export const RESPONSE_EVENTS = {
|
|
||||||
'notify': notifyMessage, /** global: notifyMessage */
|
|
||||||
'validation': formValidationMessage, /** global: formValidationMessage */
|
|
||||||
'redirect': redirectMessage, /** global: redirectMessage */
|
|
||||||
'reload': reloadButtonAction /** global: reloadButtonAction */
|
|
||||||
};
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
/** global: jsOMS */
|
|
||||||
export const TOUCH_EVENTS = {};
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
import { Logger } from '../../../../jsOMS/Log/Logger.js';
|
|
||||||
|
|
||||||
/** global: jsOMS */
|
|
||||||
export const VOICE_EVENTS = {
|
|
||||||
'login': function() { document.getElementById('iCameraLoginButton').click(); },
|
|
||||||
};
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
jsOMS.ready(function ()
|
|
||||||
{
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const logo = document.getElementById('login-logo');
|
|
||||||
const cLogin = document.getElementById('iCameraLoginButton');
|
|
||||||
const pLogin = document.getElementById('iPasswordLoginButton');
|
|
||||||
const cancel = document.getElementsByClassName('cancelButton');
|
|
||||||
const cancelLength = cancel.length;
|
|
||||||
let timer = 10000;
|
|
||||||
|
|
||||||
cLogin.addEventListener('click', function() {
|
|
||||||
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
|
||||||
const clock = document.getElementById('iCameraCountdownClock');
|
|
||||||
const countdown = document.getElementById('iCameraCountdown');
|
|
||||||
const video = document.getElementById('iVideoCanvas');
|
|
||||||
|
|
||||||
navigator.mediaDevices.getUserMedia({ video: true, audio: false }).then(function(stream) {
|
|
||||||
video.srcObject = stream;
|
|
||||||
video.play();
|
|
||||||
|
|
||||||
jsOMS.addClass(logo, 'vh');
|
|
||||||
jsOMS.addClass(cLogin, 'vh');
|
|
||||||
jsOMS.addClass(pLogin, 'vh');
|
|
||||||
jsOMS.removeClass(document.getElementById('cameraLogin'), 'vh');
|
|
||||||
|
|
||||||
timer = 10000;
|
|
||||||
clock.innerHTML = timer / 1000;
|
|
||||||
|
|
||||||
jsOMS.removeClass(countdown, 'vh');
|
|
||||||
|
|
||||||
const cameraTimer = setInterval(function() {
|
|
||||||
timer -= 100;
|
|
||||||
|
|
||||||
if (timer % 1000 === 0) {
|
|
||||||
clock.innerHTML = timer / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timer <= 0) {
|
|
||||||
jsOMS.addClass(document.getElementById('cameraLogin'), 'vh');
|
|
||||||
jsOMS.removeClass(cLogin, 'vh');
|
|
||||||
jsOMS.removeClass(pLogin, 'vh');
|
|
||||||
jsOMS.removeClass(logo, 'vh');
|
|
||||||
|
|
||||||
video.pause();
|
|
||||||
stream.getVideoTracks()[0].stop();
|
|
||||||
|
|
||||||
clearInterval(cameraTimer);
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
pLogin.addEventListener('click', function() {
|
|
||||||
const clock = document.getElementById('iPasswordCountdownClock');
|
|
||||||
const countdown = document.getElementById('iPasswordCountdown');
|
|
||||||
|
|
||||||
jsOMS.addClass(pLogin, 'vh');
|
|
||||||
jsOMS.addClass(cLogin, 'vh');
|
|
||||||
jsOMS.removeClass(document.getElementById('passwordLogin'), 'vh');
|
|
||||||
|
|
||||||
timer = 10000;
|
|
||||||
clock.innerHTML = timer / 1000;
|
|
||||||
|
|
||||||
jsOMS.removeClass(countdown, 'vh');
|
|
||||||
|
|
||||||
const passwordTimer = setInterval(function() {
|
|
||||||
timer -= 100;
|
|
||||||
|
|
||||||
if (timer % 1000 === 0) {
|
|
||||||
clock.innerHTML = timer / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timer <= 0) {
|
|
||||||
jsOMS.addClass(document.getElementById('passwordLogin'), 'vh');
|
|
||||||
jsOMS.removeClass(pLogin, 'vh');
|
|
||||||
jsOMS.removeClass(cLogin, 'vh');
|
|
||||||
|
|
||||||
clearInterval(passwordTimer);
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (let i = 0; i < cancelLength; ++i) {
|
|
||||||
cancel[i].addEventListener('click', function() {
|
|
||||||
timer = 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
import { AssetManager } from '../../../jsOMS/Asset/AssetManager.js';
|
|
||||||
import { Logger } from '../../../jsOMS/Log/Logger.js';
|
|
||||||
import { CacheManager } from '../../../jsOMS/DataStorage/CacheManager.js';
|
|
||||||
import { StorageManager } from '../../../jsOMS/DataStorage/StorageManager.js';
|
|
||||||
import { EventManager } from '../../../jsOMS/Event/EventManager.js';
|
|
||||||
import { ResponseManager } from '../../../jsOMS/Message/Response/ResponseManager.js';
|
|
||||||
import { Dispatcher } from '../../../jsOMS/Dispatcher/Dispatcher.js';
|
|
||||||
import { AccountManager } from '../../../jsOMS/Account/AccountManager.js';
|
|
||||||
import { UIManager } from '../../../jsOMS/UI/UIManager.js';
|
|
||||||
import { InputManager } from '../../../jsOMS/UI/Input/InputManager.js';
|
|
||||||
import { ModuleManager } from '../../../jsOMS/Module/ModuleManager.js';
|
|
||||||
import { ReadManager } from '../../../jsOMS/UI/Input/Voice/ReadManager.js';
|
|
||||||
import { VoiceManager } from '../../../jsOMS/UI/Input/Voice/VoiceManager.js';
|
|
||||||
import { NotificationManager } from '../../../jsOMS/Message/Notification/NotificationManager.js';
|
|
||||||
import { HttpUri } from '../../../jsOMS/Uri/HttpUri.js';
|
|
||||||
import { UriFactory } from '../../../jsOMS/Uri/UriFactory.js';
|
|
||||||
|
|
||||||
import { ACTION_EVENTS } from './global/ActionEvents.js';
|
|
||||||
import { KEYBOARD_EVENTS } from './global/KeyboardEvents.js';
|
|
||||||
import { MOUSE_EVENTS } from './global/MouseEvents.js';
|
|
||||||
import { RESPONSE_EVENTS } from './global/ResponseEvents.js';
|
|
||||||
import { TOUCH_EVENTS } from './global/TouchEvents.js';
|
|
||||||
import { VOICE_EVENTS } from './global/VoiceEvents.js';
|
|
||||||
|
|
||||||
export class Application {
|
|
||||||
constructor ()
|
|
||||||
{
|
|
||||||
//jsOMS.Autoloader.initPreloaded();
|
|
||||||
|
|
||||||
this.logger = Logger.getInstance();
|
|
||||||
window.logger = this.logger;
|
|
||||||
|
|
||||||
this.cacheManager = new CacheManager();
|
|
||||||
this.storageManager = new StorageManager();
|
|
||||||
this.eventManager = new EventManager();
|
|
||||||
this.responseManager = new ResponseManager();
|
|
||||||
this.dispatcher = new Dispatcher();
|
|
||||||
this.assetManager = new AssetManager();
|
|
||||||
this.accountManager = new AccountManager();
|
|
||||||
this.uiManager = new UIManager(this);
|
|
||||||
this.inputManager = new InputManager(this);
|
|
||||||
this.moduleManager = new ModuleManager(this);
|
|
||||||
this.readManager = new ReadManager();
|
|
||||||
this.voiceManager = new VoiceManager(this);
|
|
||||||
this.notifyManager = new NotificationManager();
|
|
||||||
this.request = new HttpUri(window.location.href);
|
|
||||||
|
|
||||||
this.request.setRootPath(
|
|
||||||
HttpUri.parseUrl(
|
|
||||||
typeof document.getElementsByTagName('base')[0] !== 'undefined'
|
|
||||||
? document.getElementsByTagName('base')[0].href
|
|
||||||
: ''
|
|
||||||
).path
|
|
||||||
);
|
|
||||||
|
|
||||||
this.setResponseMessages();
|
|
||||||
|
|
||||||
this.setActions();
|
|
||||||
this.setKeyboardActions();
|
|
||||||
this.setMouseActions();
|
|
||||||
this.setVoiceActions();
|
|
||||||
|
|
||||||
UriFactory.setupUriBuilder(this.request);
|
|
||||||
UriFactory.setQuery('/lang', window.location.href.substr(this.request.getBase().length).split('/')[0]);
|
|
||||||
|
|
||||||
this.uiManager.bind();
|
|
||||||
};
|
|
||||||
|
|
||||||
setResponseMessages ()
|
|
||||||
{
|
|
||||||
/** global: RESPONSE_EVENTS */
|
|
||||||
for (const key in RESPONSE_EVENTS) {
|
|
||||||
if (RESPONSE_EVENTS.hasOwnProperty(key)) {
|
|
||||||
this.responseManager.add(key, RESPONSE_EVENTS[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
setActions ()
|
|
||||||
{
|
|
||||||
/** global: ACTION_EVENTS */
|
|
||||||
for (const key in ACTION_EVENTS) {
|
|
||||||
if (ACTION_EVENTS.hasOwnProperty(key)) {
|
|
||||||
this.uiManager.getActionManager().add(key, ACTION_EVENTS[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
setKeyboardActions ()
|
|
||||||
{
|
|
||||||
/** global: KEYBOARD_EVENTS */
|
|
||||||
let length = KEYBOARD_EVENTS.length;
|
|
||||||
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
this.inputManager.getKeyboardManager().add(
|
|
||||||
KEYBOARD_EVENTS[i]['element'],
|
|
||||||
KEYBOARD_EVENTS[i]['keys'],
|
|
||||||
KEYBOARD_EVENTS[i]['callback']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
setMouseActions ()
|
|
||||||
{
|
|
||||||
/** global: MOUSE_EVENTS */
|
|
||||||
let length = MOUSE_EVENTS.length;
|
|
||||||
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
this.inputManager.getMouseManager().add(
|
|
||||||
MOUSE_EVENTS[i]['element'],
|
|
||||||
MOUSE_EVENTS[i]['type'],
|
|
||||||
MOUSE_EVENTS[i]['button'],
|
|
||||||
MOUSE_EVENTS[i]['callback'],
|
|
||||||
MOUSE_EVENTS[i]['exact']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
setVoiceActions ()
|
|
||||||
{
|
|
||||||
/** global: VOICE_EVENTS */
|
|
||||||
for (const key in VOICE_EVENTS) {
|
|
||||||
if (VOICE_EVENTS.hasOwnProperty(key)) {
|
|
||||||
this.voiceManager.add(key, VOICE_EVENTS[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.voiceManager.setup();
|
|
||||||
this.voiceManager.start();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
jsOMS.ready(function ()
|
|
||||||
{
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/** global: jsOMS */
|
|
||||||
window.omsApp = new Application();
|
|
||||||
});
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Jingga
|
|
||||||
*
|
|
||||||
* PHP Version 8.2
|
|
||||||
*
|
|
||||||
* @package Web\Timerecording
|
|
||||||
* @copyright Dennis Eichhorn
|
|
||||||
* @license OMS License 2.0
|
|
||||||
* @version 1.0.0
|
|
||||||
* @link https://jingga.app
|
|
||||||
*/
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
return [[
|
|
||||||
'CameraLogin' => 'Camera Login',
|
|
||||||
'Cancel' => 'Cancel',
|
|
||||||
'IDCard' => 'ID Card',
|
|
||||||
'Login' => 'Login',
|
|
||||||
'Logo' => 'Logo',
|
|
||||||
'Password' => 'Password',
|
|
||||||
'PasswordLogin' => 'Password Login',
|
|
||||||
'TimerCamera' => 'Camera turns off in: %s (s)',
|
|
||||||
'TimerLogin' => 'Login turns off in: %s (s)',
|
|
||||||
'Username' => 'Username',
|
|
||||||
':meta' => 'Time recording application',
|
|
||||||
]];
|
|
||||||
|
|
@ -1,267 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Jingga
|
|
||||||
*
|
|
||||||
* PHP Version 8.2
|
|
||||||
*
|
|
||||||
* @package Template
|
|
||||||
* @copyright Dennis Eichhorn
|
|
||||||
* @license OMS License 2.0
|
|
||||||
* @version 1.0.0
|
|
||||||
* @link https://jingga.app
|
|
||||||
*/
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Jingga
|
|
||||||
*
|
|
||||||
* PHP Version 8.2
|
|
||||||
*
|
|
||||||
* @package Web\Timerecording
|
|
||||||
* @copyright Dennis Eichhorn
|
|
||||||
* @license OMS License 2.0
|
|
||||||
* @version 1.0.0
|
|
||||||
* @link https://jingga.app
|
|
||||||
*/
|
|
||||||
$head = $this->head;
|
|
||||||
?>
|
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html lang="<?= $this->printHtml($this->response->header->l11n->language); ?>">
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<base href="<?= \phpOMS\Uri\UriFactory::build('{/base}'); ?>/">
|
|
||||||
<meta name="theme-color" content="#9e51c5">
|
|
||||||
<meta name="msapplication-navbutton-color" content="#9e51c5">
|
|
||||||
<meta name="theme-color" content="#9e51c5">
|
|
||||||
<meta name="description" content="<?= $this->getHtml(':meta', '0', '0'); ?>">
|
|
||||||
<link rel="manifest" href="<?= \phpOMS\Uri\UriFactory::build('Web/Timerecording/manifest.json'); ?>">
|
|
||||||
<link rel="shortcut icon" href="<?= \phpOMS\Uri\UriFactory::build('Web/Timerecording/img/favicon.ico'); ?>" type="image/x-icon">
|
|
||||||
<?= $head->meta->render(); ?>
|
|
||||||
<title><?= $this->printHtml($head->title); ?></title>
|
|
||||||
<style><?= $head->renderStyle(); ?></style>
|
|
||||||
<script><?= $head->renderScript(); ?></script>
|
|
||||||
<?= $head->renderAssets(); ?>
|
|
||||||
<style type="text/css">
|
|
||||||
:root {
|
|
||||||
--main-bg: #2e1a5a;
|
|
||||||
--main-bg-hl: #9e51c5;
|
|
||||||
|
|
||||||
--iborder: rgba(166, 135, 232, .4);
|
|
||||||
--iborder-active: rgba(166, 135, 232, .7);
|
|
||||||
--ipt-c: rgba(166, 135, 232, .6);
|
|
||||||
--ipt-c-active: rgba(166, 135, 232, .8);
|
|
||||||
|
|
||||||
--ipt-ico-c: rgba(166, 135, 232, .6);
|
|
||||||
--ipt-ico-c-active: rgba(166, 135, 232, 1);
|
|
||||||
|
|
||||||
--btn-main-bg: rgba(166, 135, 232, .6);
|
|
||||||
--btn-main-bg-active: rgba(166, 135, 232, .8);
|
|
||||||
--btn-main-c: rgba(255, 255, 255, .9);
|
|
||||||
|
|
||||||
--txt-on-bg-c: rgba(255, 255, 255, 0.7);
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
height: 100%;
|
|
||||||
font-family: 'Roboto', sans-serif;
|
|
||||||
background-image: linear-gradient(var(--main-bg-hl), var(--main-bg));
|
|
||||||
color: var(--txt-on-bg-c);
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
flex-direction: column;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-container {
|
|
||||||
width: 90%;
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-logo {
|
|
||||||
height: 185px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-logo img {
|
|
||||||
width: 20%;
|
|
||||||
min-width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
text-shadow: 2px 2px 3px rgba(0,0,0,0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-logo {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-logo, #login-form {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#passwordLogin, #cameraLogin {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
display: inline-block;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
form label {
|
|
||||||
text-shadow: none;
|
|
||||||
color: var(--txt-on-bg-c);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
form input[type=text],
|
|
||||||
form input[type=password] {
|
|
||||||
margin-bottom: .5rem;
|
|
||||||
background: rgba(0, 0, 0, .15);
|
|
||||||
border: 1px solid var(--iborder);
|
|
||||||
text-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
color: var(--txt-on-bg-c);
|
|
||||||
width: 100%;
|
|
||||||
transition : border 500ms ease-out;
|
|
||||||
outline: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
line-height: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputWithIcon {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputWithIcon input {
|
|
||||||
padding-left: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputWithIcon .frontIco {
|
|
||||||
color: var(--ipt-ico-c);
|
|
||||||
font-size: 1rem;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
padding: .65rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputWithIcon .endIco {
|
|
||||||
color: var(--ipt-ico-c);
|
|
||||||
font-size: 1rem;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
padding: .65rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
form input[type=text]:active, form input[type=text]:focus,
|
|
||||||
form input[type=password]:active, form input[type=password]:focus {
|
|
||||||
border: 1px solid var(--iborder-active);
|
|
||||||
color: var(--txt-on-bg-c);
|
|
||||||
}
|
|
||||||
|
|
||||||
form input[type=text]:active~.frontIco, form input[type=text]:focus~.frontIco,
|
|
||||||
form input[type=password]:active~.frontIco, form input[type=password]:focus~.frontIco,
|
|
||||||
form input[type=text]:active~.endIco, form input[type=text]:focus~.endIco,
|
|
||||||
form input[type=password]:active~.endIco, form input[type=password]:focus~.endIco {
|
|
||||||
color: var(--ipt-ico-c-active);
|
|
||||||
}
|
|
||||||
|
|
||||||
form input[type=text]~.endIco, form input[type=text]~.endIco,
|
|
||||||
form input[type=password]~.endIco, form input[type=password]~.endIco {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
form input[type=submit], button {
|
|
||||||
width: calc(50% - 10px);
|
|
||||||
background-color: var(--btn-main-bg);
|
|
||||||
border: none;
|
|
||||||
text-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
color: var(--btn-main-c);
|
|
||||||
cursor: pointer;
|
|
||||||
transition : background-color 500ms ease-out;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
button+button, input+button {
|
|
||||||
margin-left: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
form input[type=submit]:hover, button:hover,
|
|
||||||
form input[type=submit]:focus, button:focus {
|
|
||||||
background-color: var(--btn-main-bg-active);
|
|
||||||
border: none;
|
|
||||||
text-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#forgot-password {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#forgot-password a {
|
|
||||||
padding-bottom: .5rem;
|
|
||||||
cursor: pointer;
|
|
||||||
transition : border-bottom 100ms ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#forgot-password a:hover,
|
|
||||||
#forgot-password a:focus {
|
|
||||||
color: rgba(255, 255, 255, .8);
|
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, .6);
|
|
||||||
}
|
|
||||||
|
|
||||||
video {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border: 1px solid var(--iborder);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="login-container">
|
|
||||||
<div id="login-logo">
|
|
||||||
<img class="animated infinte pulse" alt="<?= $this->getHtml('Logo', '0', '0'); ?>" src="<?= \phpOMS\Uri\UriFactory::build('Web/Backend/img/logo.png'); ?>">
|
|
||||||
</div>
|
|
||||||
<div id="login-form">
|
|
||||||
<form id="login" method="POST" action="<?= \phpOMS\Uri\UriFactory::build('{/api}login?{?}'); ?>">
|
|
||||||
<button id="iCameraLoginButton" name="cameraLoginButton" type="button" tabindex="1"><?= $this->getHtml('CameraLogin', '0', '0'); ?></button>
|
|
||||||
<button id="iPasswordLoginButton" name="passwordLoginButton" type="button" tabindex="2"><?= $this->getHtml('PasswordLogin', '0', '0'); ?></button>
|
|
||||||
<div id="cameraLogin" class="vh">
|
|
||||||
<h1><?= $this->getHtml('IDCard', '0', '0'); ?>:</h1>
|
|
||||||
<video id="iVideoCanvas"></video>
|
|
||||||
<button class="cancelButton" name="cancelButton" type="button" tabindex="6"><?= $this->getHtml('Cancel', '0', '0'); ?></button>
|
|
||||||
<div id="iCameraCountdown"><?php \printf($this->getHtml('TimerCamera', '0', '0'), '<span id="iCameraCountdownClock"></span>'); ?></div>
|
|
||||||
</div>
|
|
||||||
<div id="passwordLogin" class="vh">
|
|
||||||
<h1><?= $this->getHtml('Login', '0', '0'); ?>:</h1>
|
|
||||||
<label for="iName"><?= $this->getHtml('Username', '0', '0'); ?>:</label>
|
|
||||||
<div class="inputWithIcon">
|
|
||||||
<i class="frontIco g-icon" aria-hidden="true">person</i>
|
|
||||||
<input id="iName" type="text" name="user" tabindex="3" value="admin" autofocus>
|
|
||||||
<i class="endIco g-icon close" aria-hidden="true">close</i>
|
|
||||||
</div>
|
|
||||||
<label for="iPassword"><?= $this->getHtml('Password', '0', '0'); ?>:</label>
|
|
||||||
<div class="inputWithIcon">
|
|
||||||
<i class="frontIco g-icon" aria-hidden="true">lock</i>
|
|
||||||
<input id="iPassword" type="password" name="pass" tabindex="4" value="orange">
|
|
||||||
<i class="endIco g-icon close" aria-hidden="true">close</i>
|
|
||||||
</div>
|
|
||||||
<input id="iLoginButton" name="loginButton" type="submit" value="<?= $this->getHtml('Login', '0', '0'); ?>" tabindex="5">
|
|
||||||
<button class="cancelButton" name="cancelButton" type="button" tabindex="6"><?= $this->getHtml('Cancel', '0', '0'); ?></button>
|
|
||||||
<div id="iPasswordCountdown"><?php \printf($this->getHtml('TimerLogin', '0', '0'), '<span id="iPasswordCountdownClock"></span>'); ?></div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?= $head->renderAssetsLate(); ?>
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
{
|
|
||||||
"lang": "en",
|
|
||||||
"dir": "ltr",
|
|
||||||
"start_url": "../../en/timerecording",
|
|
||||||
"type": "privileged",
|
|
||||||
"name": "Jingga Time Recording",
|
|
||||||
"description": "OMS timerecording application.",
|
|
||||||
"short_name": "OMS Backend",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "/Web/Timerecording/img/logo.png",
|
|
||||||
"sizes": "64x64 128x128 256x256 512x512",
|
|
||||||
"type": "image/png",
|
|
||||||
"purpose": "any maskable"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"scope": "/",
|
|
||||||
"display": "standalone",
|
|
||||||
"orientation": "any",
|
|
||||||
"theme_color": "#712b91",
|
|
||||||
"background_color": "white",
|
|
||||||
"permissions": {
|
|
||||||
"audio-capture" : {
|
|
||||||
"description" : "Audio capture"
|
|
||||||
},
|
|
||||||
"video-capture": {
|
|
||||||
"description": "Video capture"
|
|
||||||
},
|
|
||||||
"speech-recognition" : {
|
|
||||||
"description" : "Speech recognition"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user