mirror of
https://github.com/Karaka-Management/oms-HumanResourceTimeRecording.git
synced 2026-01-10 19:38:41 +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 * *'
|
||||
|
||||
jobs:
|
||||
general_module_workflow:
|
||||
general_module_workflow_php:
|
||||
uses: Karaka-Management/Karaka/.github/workflows/php_template.yml@develop
|
||||
secrets:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
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