1.3 #1
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use DateTime;
|
||||
use JetBrains\PhpStorm\NoReturn;
|
||||
|
||||
class API
|
||||
@@ -91,7 +92,7 @@ class API
|
||||
// When building a frontend page, you can still programmatically construct a builder array
|
||||
// and set it via $_GET like so after the API class creation:
|
||||
// $_GET['builder'] = [1 => ['where' => [0 => 'permission_uuid', 1 => $permission_uuid]]];
|
||||
if ($this->request_method !== 'GET' || $this->user_type === 'frontend') {
|
||||
if ($this->request_method !== 'GET') {
|
||||
$this->disableBuilder();
|
||||
}
|
||||
|
||||
@@ -335,6 +336,14 @@ class API
|
||||
if (!is_array($value)) return false;
|
||||
return $value;
|
||||
|
||||
case 'date':
|
||||
if (!is_string($value)) return false;
|
||||
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
|
||||
return false;
|
||||
}
|
||||
[$year, $month, $day] = explode('-', $value);
|
||||
return checkdate((int)$month, (int)$day, (int)$year);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -599,12 +608,7 @@ class API
|
||||
if ($this->user_type === 'api') {
|
||||
http_response_code($code);
|
||||
header('Content-Type: application/json');
|
||||
if ($code === 200) {
|
||||
echo json_encode(reset($data));
|
||||
} else {
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
echo json_encode($data);
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -682,6 +686,12 @@ class API
|
||||
}
|
||||
}
|
||||
|
||||
protected function isValidDate(string $date): bool
|
||||
{
|
||||
$dt = DateTime::createFromFormat('Y-m-d', $date);
|
||||
return $dt && $dt->format('Y-m-d') === $date;
|
||||
}
|
||||
|
||||
protected function buildDynamicQuery(string $tableName): array
|
||||
{
|
||||
$this->allowedGetColumns = $this->loadTableColumns($tableName);
|
||||
@@ -699,20 +709,48 @@ class API
|
||||
}
|
||||
|
||||
foreach ($_GET['builder'] as $builder) {
|
||||
if (!isset($builder['where']) || !is_array($builder['where']) || count($builder['where']) !== 2) {
|
||||
continue;
|
||||
if (isset($builder['where']) && is_array($builder['where'])) {
|
||||
|
||||
if (count($builder['where']) !== 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$column = $builder['where'][0];
|
||||
$value = $builder['where'][1];
|
||||
|
||||
if (!in_array($column, $this->allowedGetColumns, true)) {
|
||||
$this->apiOutput(400, ['error' => "The column $column is not allowed."]);
|
||||
}
|
||||
|
||||
$whereClauses[] = "$column = ?";
|
||||
$types .= 's';
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
$column = $builder['where'][0];
|
||||
$value = $builder['where'][1];
|
||||
// BETWEEN (for DATE / range fields)
|
||||
if (isset($builder['between']) && is_array($builder['between'])) {
|
||||
|
||||
if (!in_array($column, $this->allowedGetColumns, true)) {
|
||||
$this->apiOutput(400, ['error' => 'The column ' . $column . ' is not allowed.']);
|
||||
if (count($builder['between']) !== 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$column = $builder['between'][0];
|
||||
$from = $builder['between'][1];
|
||||
$to = $builder['between'][2];
|
||||
|
||||
if (!in_array($column, $this->allowedGetColumns, true)) {
|
||||
$this->apiOutput(400, ['error' => "The column $column is not allowed."]);
|
||||
}
|
||||
|
||||
if (!$this->isValidDate($from) || !$this->isValidDate($to)) {
|
||||
$this->apiOutput(400, ['error' => "Invalid date format for BETWEEN."]);
|
||||
}
|
||||
|
||||
$whereClauses[] = "$column BETWEEN ? AND ?";
|
||||
$types .= 'ss';
|
||||
$values[] = $from;
|
||||
$values[] = $to;
|
||||
}
|
||||
|
||||
$whereClauses[] = "$column = ?";
|
||||
$types .= 's';
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
if (!empty($whereClauses)) {
|
||||
|
||||
@@ -8,7 +8,7 @@ require_once 'API.php';
|
||||
|
||||
class API_companies extends API
|
||||
{
|
||||
public function updateCompanyState()
|
||||
public function updateCompanyState(): void
|
||||
{
|
||||
$query = "UPDATE companies SET company_state = ? WHERE company_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
@@ -17,4 +17,13 @@ class API_companies extends API
|
||||
$this->apiOutput(200, ['success' => 'company state successfully updated']);
|
||||
}
|
||||
}
|
||||
|
||||
public function getCompanies(): array
|
||||
{
|
||||
list($query, $types, $params) = $this->buildDynamicQuery('companies');
|
||||
|
||||
$items = $this->generalGetFunction($query, $types, $params, false, 'Companies');
|
||||
|
||||
return ($items);
|
||||
}
|
||||
}
|
||||
154
pub/api/classes/API_office_travel_reimburse.php
Normal file
154
pub/api/classes/API_office_travel_reimburse.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
use JetBrains\PhpStorm\NoReturn;
|
||||
use PDO;
|
||||
use api\classes\API_companies;
|
||||
|
||||
require_once 'API.php';
|
||||
require_once 'API_companies.php';
|
||||
|
||||
class API_office_travel_reimburse extends API
|
||||
{
|
||||
#[NoReturn]
|
||||
public function createTravelReimburse(): void
|
||||
{
|
||||
$query = " INSERT INTO office_travelreimburse (
|
||||
reimburse_uuid,
|
||||
user_uuid,
|
||||
departure_postcode,
|
||||
destination_postcode,
|
||||
travel_distance,
|
||||
travel_description,
|
||||
travel_date,
|
||||
office_travelreimburse_company_uuid,
|
||||
office_travelreimburse_company_name,
|
||||
homework
|
||||
)
|
||||
VALUES (UUID(),
|
||||
:user_uuid,
|
||||
:departure_postcode,
|
||||
:destination_postcode,
|
||||
:travel_distance,
|
||||
:travel_description,
|
||||
:travel_date,
|
||||
:office_travelreimburse_company_uuid,
|
||||
:office_travelreimburse_company_name,
|
||||
:homework
|
||||
)";
|
||||
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
|
||||
$stmt->bindValue(':user_uuid', $this->data['user_uuid']);
|
||||
$stmt->bindValue(':departure_postcode', $this->data['departure_postcode']);
|
||||
$stmt->bindValue(':destination_postcode', $this->data['destination_postcode']);
|
||||
$stmt->bindValue(':travel_distance', $this->data['travel_distance']);
|
||||
$stmt->bindValue(':travel_date', $this->data['travel_date']);
|
||||
$stmt->bindValue(':office_travelreimburse_company_uuid', $this->data['office_travelreimburse_company_uuid']);
|
||||
$stmt->bindValue(':office_travelreimburse_company_name', $this->data['office_travelreimburse_company_name']);
|
||||
$stmt->bindValue(':homework', $this->data['homework']);
|
||||
|
||||
// Optional fields
|
||||
$stmt->bindValue(':travel_description', $this->data['travel_description'] ?? null, $this->data['travel_description'] ?? null ? PDO::PARAM_STR : PDO::PARAM_NULL);
|
||||
|
||||
$stmt->execute();
|
||||
|
||||
$this->apiOutput(200, ['success' => 'Travel reimburse added.']);
|
||||
}
|
||||
|
||||
public function getTravelReimburse(): array
|
||||
{
|
||||
list($query, $types, $params) = $this->buildDynamicQuery('office_travelreimburse');
|
||||
|
||||
$items = $this->generalGetFunction($query, $types, $params, false, 'TravelReimburse');
|
||||
|
||||
return ($items);
|
||||
}
|
||||
|
||||
#[NoReturn]
|
||||
public function updateTravelReimburse(): void
|
||||
{
|
||||
$query = "UPDATE office_travelreimburse SET
|
||||
user_uuid = :user_uuid,
|
||||
departure_postcode = :departure_postcode,
|
||||
destination_postcode = :destination_postcode,
|
||||
travel_date = :travel_date,
|
||||
travel_distance = :travel_distance,
|
||||
travel_description = :travel_description,
|
||||
office_travelreimburse_company_uuid = :office_travelreimburse_company_uuid,
|
||||
office_travelreimburse_company_name = :office_travelreimburse_company_name,
|
||||
homework = :homework
|
||||
WHERE reimburse_uuid = :reimburse_uuid";
|
||||
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
|
||||
$stmt->bindValue(':reimburse_uuid', $this->data['reimburse_uuid'], PDO::PARAM_STR);
|
||||
$stmt->bindValue(':user_uuid', $this->data['user_uuid'], PDO::PARAM_STR);
|
||||
$stmt->bindValue(':departure_postcode', $this->data['departure_postcode'], PDO::PARAM_STR);
|
||||
$stmt->bindValue(':destination_postcode', $this->data['destination_postcode'], PDO::PARAM_STR);
|
||||
$stmt->bindValue(':travel_date', $this->data['travel_date'], PDO::PARAM_STR);
|
||||
$stmt->bindValue(':travel_distance', $this->data['travel_distance'], PDO::PARAM_INT);
|
||||
$stmt->bindValue(':office_travelreimburse_company_uuid', $this->data['office_travelreimburse_company_uuid'], PDO::PARAM_STR);
|
||||
$stmt->bindValue(':office_travelreimburse_company_name', $this->data['office_travelreimburse_company_name'], PDO::PARAM_STR);
|
||||
$stmt->bindValue(':homework', $this->data['homework'], PDO::PARAM_BOOL);
|
||||
|
||||
// Optional fields
|
||||
$stmt->bindValue(':travel_description', $this->data['travel_description'] ?? null, isset($this->data['travel_description']) && $this->data['travel_description'] !== null ? PDO::PARAM_STR : PDO::PARAM_NULL);
|
||||
|
||||
$stmt->execute();
|
||||
|
||||
if ($stmt->rowCount() === 0) {
|
||||
$check = $this->pdo->prepare(
|
||||
"SELECT 1 FROM office_travelreimburse WHERE reimburse_uuid = :uuid"
|
||||
);
|
||||
$check->execute([
|
||||
':uuid' => $this->data['reimburse_uuid']
|
||||
]);
|
||||
|
||||
if (!$check->fetchColumn()) {
|
||||
$this->apiOutput(404, ['error' => 'Travel reimbursement not found.']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->apiOutput(200, ['success' => 'Travel reimbursement updated.']);
|
||||
}
|
||||
|
||||
#[NoReturn]
|
||||
public function deleteTravelReimburse(): void
|
||||
{
|
||||
$query = "DELETE FROM office_travelreimburse WHERE reimburse_uuid = ? AND user_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('ss', $this->data['reimburse_uuid'], $_SESSION['user']['user_uuid']);
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
if ($stmt->affected_rows === 0) {
|
||||
$stmt->close();
|
||||
$this->apiOutput(403, ['error' => 'Travel reimbursement not found or you do not have permission to delete it.']);
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
|
||||
$this->apiOutput(200, ['success' => 'Travel reimburse removed.']);
|
||||
}
|
||||
|
||||
public function setTravelReimburseCompany(): void
|
||||
{
|
||||
$API_companies = new API_companies();
|
||||
if (!empty($GLOBALS['modules_enabled']['customers'])) {
|
||||
|
||||
$companies = $API_companies->getCompanies();
|
||||
|
||||
$companyUuid = $this->postedData['office_travelreimburse_company_uuid'] ?? null;
|
||||
if ($companyUuid) {
|
||||
foreach ($companies as $company) {
|
||||
if ($company['company_uuid'] === $companyUuid) {
|
||||
$this->postedData['office_travelreimburse_company_name'] = $company['company_name'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,4 +28,30 @@ class API_portalsettings extends API
|
||||
|
||||
return ($items);
|
||||
}
|
||||
|
||||
public function updatePortalSettingsOfficeTravelReimburse()
|
||||
{
|
||||
$setParts = [
|
||||
'office_travelreimburse_cents_homework_incl = :homework_incl',
|
||||
'office_travelreimburse_cents_homework_excl = :homework_excl',
|
||||
'office_travelreimburse_cents_business_incl = :business_incl',
|
||||
'office_travelreimburse_cents_business_excl = :business_excl',
|
||||
];
|
||||
|
||||
$params = [
|
||||
':homework_incl' => $this->data['office_travelreimburse_cents_homework_incl'],
|
||||
':homework_excl' => $this->data['office_travelreimburse_cents_homework_excl'],
|
||||
':business_incl' => $this->data['office_travelreimburse_cents_business_incl'],
|
||||
':business_excl' => $this->data['office_travelreimburse_cents_business_excl'],
|
||||
':portal_uuid' => $this->data['portal_uuid'],
|
||||
];
|
||||
|
||||
$query = "UPDATE system_settings SET " . implode(", ", $setParts) . "
|
||||
WHERE portal_uuid = :portal_uuid";
|
||||
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
$stmt->execute($params);
|
||||
|
||||
$this->apiOutput(200, ['success' => 'portal settings updated successfully.']);
|
||||
}
|
||||
}
|
||||
24
pub/api/v1/customers/companies/index.php
Normal file
24
pub/api/v1/customers/companies/index.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use api\classes\API_companies;
|
||||
|
||||
session_start();
|
||||
|
||||
require_once "{$_SERVER['DOCUMENT_ROOT']}/api/classes/API_companies.php";
|
||||
|
||||
|
||||
$API_companies = new API_companies();
|
||||
|
||||
if (!$GLOBALS['modules_enabled']['customers']) {
|
||||
echo '405 Not Allowed';
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($API_companies->request_method === 'GET') {
|
||||
$API_companies->checkPermissions('customer-companies', 'RO');
|
||||
|
||||
$companies = $API_companies->getCompanies();
|
||||
|
||||
$API_companies->apiOutput($code = 200, $data = $companies);
|
||||
|
||||
}
|
||||
51
pub/api/v1/office/travel-reimburse/configure/index.php
Normal file
51
pub/api/v1/office/travel-reimburse/configure/index.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
use api\classes\API_portalsettings;
|
||||
use api\classes\API_office_travel_reimburse;
|
||||
|
||||
session_start();
|
||||
|
||||
require_once "{$_SERVER['DOCUMENT_ROOT']}/api/classes/API_portalsettings.php";
|
||||
require_once "{$_SERVER['DOCUMENT_ROOT']}/api/classes/API_office_travel_reimburse.php";
|
||||
|
||||
|
||||
$API_portalsettings = new API_portalsettings();
|
||||
|
||||
if (!$GLOBALS['modules_enabled']['office']) {
|
||||
echo '405 Not Allowed';
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($API_portalsettings->request_method === 'GET') {
|
||||
$API_portalsettings->checkPermissions('office-travel-reimburse', 'RO');
|
||||
|
||||
$travel_reimbursements = $API_portalsettings->getTravelReimburseSettings();
|
||||
|
||||
$API_portalsettings->apiOutput($code = 200, $travel_reimbursements);
|
||||
|
||||
} elseif ($API_portalsettings->request_method === 'PUT') {
|
||||
|
||||
$API_portalsettings->checkPermissions('admin-portalsettings', 'RW');
|
||||
|
||||
$API_office_travel_reimburse = new API_office_travel_reimburse();
|
||||
|
||||
# Edit the portal settings of the platform
|
||||
$requiredFields = [
|
||||
'office_travelreimburse_cents_homework_incl' => ['type' => 'int'],
|
||||
'office_travelreimburse_cents_homework_excl' => ['type' => 'int'],
|
||||
'office_travelreimburse_cents_business_incl' => ['type' => 'int'],
|
||||
'office_travelreimburse_cents_business_excl' => ['type' => 'int'],
|
||||
'portal_uuid' => ['type' => 'uuid'],
|
||||
];
|
||||
|
||||
if (count($API_portalsettings->getPortalSettings()) !== 1) {
|
||||
$API_portalsettings->apiOutput(400, ['error' => 'Invalid portal_uuid']);
|
||||
}
|
||||
|
||||
$API_portalsettings->validateData($requiredFields);
|
||||
|
||||
# Update the permission
|
||||
$API_portalsettings->updatePortalSettingsOfficeTravelReimburse();
|
||||
|
||||
|
||||
}
|
||||
89
pub/api/v1/office/travel-reimburse/index.php
Normal file
89
pub/api/v1/office/travel-reimburse/index.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
use api\classes\API_office_travel_reimburse;
|
||||
|
||||
session_start();
|
||||
|
||||
require_once "{$_SERVER['DOCUMENT_ROOT']}/api/classes/API_office_travel_reimburse.php";
|
||||
require_once "{$_SERVER['DOCUMENT_ROOT']}/api/classes/API_companies.php";
|
||||
|
||||
$API_office_travel_reimburse = new API_office_travel_reimburse();
|
||||
|
||||
if (!$GLOBALS['modules_enabled']['office']) {
|
||||
echo '405 Not Allowed';
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($API_office_travel_reimburse->request_method === 'GET') {
|
||||
$API_office_travel_reimburse->checkPermissions('office-travel-reimburse', 'RO');
|
||||
|
||||
$_GET['builder'] = [1 => ['where' => [0 => 'user_uuid', 1 => $_SESSION['user']['user_uuid']]]];
|
||||
|
||||
$travel_reimbursements = $API_office_travel_reimburse->getTravelReimburse();
|
||||
$API_office_travel_reimburse->apiOutput($code = 200, $travel_reimbursements);
|
||||
|
||||
} elseif ($API_office_travel_reimburse->request_method === 'POST') {
|
||||
|
||||
$API_office_travel_reimburse->checkPermissions('office-travel-reimburse', 'RW');
|
||||
$API_office_travel_reimburse->return_url = false;
|
||||
$API_office_travel_reimburse->setTravelReimburseCompany();
|
||||
$API_office_travel_reimburse->postedData['user_uuid'] = $_SESSION['user']['user_uuid'];
|
||||
|
||||
$requiredFields = [
|
||||
'user_uuid' => ['type' => 'uuid'],
|
||||
'departure_postcode' => ['type' => 'string'],
|
||||
'destination_postcode' => ['type' => 'string'],
|
||||
'travel_date' => ['type' => 'date'],
|
||||
'travel_distance' => ['type' => 'int'],
|
||||
'office_travelreimburse_company_uuid' => ['type' => 'uuid'],
|
||||
'office_travelreimburse_company_name' => ['type' => 'string'],
|
||||
'homework' => ['type' => 'int']
|
||||
];
|
||||
|
||||
$optionalFields = [
|
||||
'travel_description' => ['type' => 'string']
|
||||
];
|
||||
|
||||
$API_office_travel_reimburse->validateData($requiredFields, $optionalFields);
|
||||
$API_office_travel_reimburse->createTravelReimburse();
|
||||
|
||||
} elseif ($API_office_travel_reimburse->request_method === 'PUT') {
|
||||
# Only superuser can delete permission due to fact that the backend needs programming when setting a permission
|
||||
|
||||
$API_office_travel_reimburse->checkPermissions('office-travel-reimburse', 'RW');
|
||||
$API_office_travel_reimburse->return_url = false;
|
||||
$API_office_travel_reimburse->setTravelReimburseCompany();
|
||||
$API_office_travel_reimburse->postedData['user_uuid'] = $_SESSION['user']['user_uuid'];
|
||||
|
||||
$requiredFields = [
|
||||
'reimburse_uuid' => ['type' => 'uuid'],
|
||||
'user_uuid' => ['type' => 'uuid'],
|
||||
'departure_postcode' => ['type' => 'string'],
|
||||
'destination_postcode' => ['type' => 'string'],
|
||||
'travel_date' => ['type' => 'date'],
|
||||
'travel_distance' => ['type' => 'int'],
|
||||
'office_travelreimburse_company_uuid' => ['type' => 'uuid'],
|
||||
'office_travelreimburse_company_name' => ['type' => 'string'],
|
||||
'homework' => ['type' => 'boolean']
|
||||
];
|
||||
|
||||
$optionalFields = [
|
||||
'travel_description' => ['type' => 'string'],
|
||||
];
|
||||
|
||||
$API_office_travel_reimburse->validateData($requiredFields, $optionalFields);
|
||||
$API_office_travel_reimburse->updateTravelReimburse();
|
||||
|
||||
} elseif ($API_office_travel_reimburse->request_method === 'DELETE') {
|
||||
# Only superuser can delete permission due to fact that the backend needs programming when setting a permission
|
||||
$API_office_travel_reimburse->checkPermissions('office-travel-reimburse', 'RW');
|
||||
$API_office_travel_reimburse->return_url = false;
|
||||
|
||||
$requiredFields = [
|
||||
'reimburse_uuid' => ['type' => 'uuid']
|
||||
];
|
||||
|
||||
$API_office_travel_reimburse->validateData($requiredFields);
|
||||
$API_office_travel_reimburse->deleteTravelReimburse();
|
||||
|
||||
}
|
||||
211
pub/api/v1/office/travel-reimburse/send/index.php
Normal file
211
pub/api/v1/office/travel-reimburse/send/index.php
Normal file
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
use api\classes\API_office_travel_reimburse;
|
||||
use bin\php\Classes\mailBuilder;
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Options;
|
||||
|
||||
session_start();
|
||||
|
||||
require_once "{$_SERVER['DOCUMENT_ROOT']}/../vendor/autoload.php";
|
||||
require_once "{$_SERVER['DOCUMENT_ROOT']}/api/classes/API_office_travel_reimburse.php";
|
||||
require_once "{$_SERVER['DOCUMENT_ROOT']}/api/classes/API_companies.php";
|
||||
require_once "{$_SERVER['DOCUMENT_ROOT']}/bin/php/Classes/mailBuilder.php";
|
||||
|
||||
$API_office_travel_reimburse = new API_office_travel_reimburse();
|
||||
|
||||
if (!$GLOBALS['modules_enabled']['office']) {
|
||||
echo '405 Not Allowed';
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($API_office_travel_reimburse->request_method === 'POST') {
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
$API_office_travel_reimburse->checkPermissions('office-travel-reimburse', 'RO');
|
||||
$API_office_travel_reimburse->return_url = false;
|
||||
$portal_settings = $GLOBALS['conn']->query("SELECT * FROM system_settings")->fetch_assoc();
|
||||
$month = $_POST['calender_month'];
|
||||
$firstDay = $month . '-01';
|
||||
$lastDay = date('Y-m-t', strtotime($firstDay));
|
||||
|
||||
$_GET['builder'] = [
|
||||
1 => [
|
||||
'where' => [
|
||||
0 => 'user_uuid',
|
||||
1 => $_SESSION['user']['user_uuid']
|
||||
],
|
||||
'between' => [
|
||||
0 => 'travel_date',
|
||||
1 => $firstDay,
|
||||
2 => $lastDay
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$travel_reimbursements = $API_office_travel_reimburse->getTravelReimburse();
|
||||
|
||||
// format is this: 2026-07
|
||||
$month = $_POST['calender_month'];
|
||||
|
||||
$lastDay = date('Y-m-t', strtotime($month . '-01'));
|
||||
$invoiceNumber = 'D-' . date('nY', strtotime($month . '-01'));
|
||||
|
||||
$totalBusinessKm = 0;
|
||||
$totalHomeworkKm = 0;
|
||||
|
||||
foreach ($travel_reimbursements as $trip) {
|
||||
if ($trip['homework']) {
|
||||
$totalHomeworkKm += $trip['travel_distance'];
|
||||
} else {
|
||||
$totalBusinessKm += $trip['travel_distance'];
|
||||
}
|
||||
}
|
||||
|
||||
$totalKm = $totalBusinessKm + $totalHomeworkKm;
|
||||
|
||||
$amountExcl =
|
||||
($totalBusinessKm * $portal_settings['office_travelreimburse_cents_business_excl']) +
|
||||
($totalHomeworkKm * $portal_settings['office_travelreimburse_cents_homework_excl']);
|
||||
|
||||
$amountIncl =
|
||||
($totalBusinessKm * $portal_settings['office_travelreimburse_cents_business_incl']) +
|
||||
($totalHomeworkKm * $portal_settings['office_travelreimburse_cents_homework_incl']);
|
||||
|
||||
// cents -> euros
|
||||
$amountExcl /= 100;
|
||||
$amountIncl /= 100;
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<h2><?= __('travelReimburse') ?></h2>
|
||||
|
||||
<table border="0" cellpadding="4">
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?= __('date') ?>:</strong>
|
||||
</td>
|
||||
<td><?= date('d/m/Y', strtotime($lastDay)); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?= __('full_name') ?>:</strong>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($_SESSION['user']['user_full_name']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?= __('invoice_number') ?>:</strong>
|
||||
</td>
|
||||
<td><?= $invoiceNumber; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?= __('total_travel_excl') ?>:</strong>
|
||||
</td>
|
||||
<td>€ <?= number_format($amountExcl, 2, ',', '.'); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?= __('total_travel_incl') ?>:</strong>
|
||||
</td>
|
||||
<td>€ <?= number_format($amountIncl, 2, ',', '.'); ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
|
||||
<table border="1" cellpadding="5" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?= __('date') ?></th>
|
||||
<th><?= __('from') ?></th>
|
||||
<th><?= __('to') ?></th>
|
||||
<th><?= __('company') ?></th>
|
||||
<th>KM <?= __('homework') ?></th>
|
||||
<th>KM <?= __('business') ?></th>
|
||||
<th><?= __('description') ?></th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
<?php foreach ($travel_reimbursements as $trip) { ?>
|
||||
|
||||
<tr>
|
||||
<td><?= date('d-m-Y', strtotime($trip['travel_date'])); ?></td>
|
||||
<td><?= htmlspecialchars($trip['departure_postcode']); ?></td>
|
||||
<td><?= htmlspecialchars($trip['destination_postcode']); ?></td>
|
||||
<td><?= htmlspecialchars($trip['office_travelreimburse_company_name']); ?></td>
|
||||
<td><?= ($trip['homework'] ? $trip['travel_distance'] : '') ?></td>
|
||||
<td><?= ($trip['homework'] ? '' : $trip['travel_distance']) ?></td>
|
||||
<td><?= $trip['travel_description']; ?></td>
|
||||
</tr>
|
||||
|
||||
<?php } ?>
|
||||
<tr>
|
||||
<td colspan="5" align="right">
|
||||
<strong><?= __('total') . ' ' . __('business') ?> km</strong>
|
||||
</td>
|
||||
<td>
|
||||
<strong><?= $totalBusinessKm; ?></strong>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="5" align="right">
|
||||
<strong><?= __('total') . ' ' . __('homework') ?> km</strong>
|
||||
</td>
|
||||
<td>
|
||||
<strong><?= $totalHomeworkKm; ?></strong>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="5" align="right">
|
||||
<strong><?= __('total') ?></strong>
|
||||
</td>
|
||||
<td>
|
||||
<strong><?= $totalKm; ?></strong>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php
|
||||
|
||||
$html = ob_get_clean();
|
||||
|
||||
$options = new Options();
|
||||
$options->set('isHtml5ParserEnabled', true);
|
||||
$options->set('isRemoteEnabled', true);
|
||||
|
||||
$dompdf = new Dompdf($options);
|
||||
$dompdf->loadHtml($html);
|
||||
$dompdf->setPaper('A4', 'portrait');
|
||||
$dompdf->render();
|
||||
$pdfFile = sys_get_temp_dir() . '/travel_reimburse.pdf';
|
||||
|
||||
file_put_contents(
|
||||
$pdfFile,
|
||||
$dompdf->output()
|
||||
);
|
||||
|
||||
$mail = new mailBuilder();
|
||||
|
||||
$mail->addAddress($_SESSION['user']['user_email'], $_SESSION['user']['user_full_name']);
|
||||
$mail->subject = 'Travel reimbursement ' . $_SESSION['user']['user_full_name'] . ' - ' . $invoiceNumber;
|
||||
$mail->mailText = 'Your travel reimbursement is attached.';
|
||||
|
||||
try {
|
||||
$mail->addAttachment($pdfFile, 'travel_reimbursement.pdf');
|
||||
} catch (\PHPMailer\PHPMailer\Exception $e) {
|
||||
|
||||
}
|
||||
|
||||
$mail->sendMail();
|
||||
|
||||
unlink($pdfFile);
|
||||
}
|
||||
@@ -39,6 +39,7 @@ return [
|
||||
'access_control' => 'Access Control',
|
||||
'monitoring' => 'Monitoring',
|
||||
'systemconfig' => 'Settings',
|
||||
'configure' => 'Configure',
|
||||
'portal-management' => 'Portal Management',
|
||||
'dashboard_text_admin_management' => 'Manage access for administrators',
|
||||
'dashboard_text_access_control' => 'Edit permission and groups',
|
||||
@@ -231,7 +232,7 @@ return [
|
||||
'autoproviosioning' => 'Autoprovisioning',
|
||||
'dashboard_text_stompjes' => 'Can you kick it?',
|
||||
'office' => 'Office',
|
||||
'stompjeslist' => 'Stompjeslist',
|
||||
'stompjeslist' => 'Stompjes',
|
||||
'stompable' => 'Stompable',
|
||||
'stompjes' => 'Stomps',
|
||||
'stomped' => 'Stomped',
|
||||
@@ -308,4 +309,25 @@ return [
|
||||
'source_inserve_sync_subscriptions' => 'Sync server subscriptions',
|
||||
'source_inserve_sync_subscriptions_desc' => 'This API call first executes the <b>Sync cloud distributor</b> companies action and then creates a subscription if it does not exist, or updates the subscription description if it does. It also creates subscription lines with the correct product_code entered in the <b>custom source data</b> JSON field.',
|
||||
'not_allowed_to_change_due_to_group_weight' => 'You are not permitted to modify these settings because has a lower group weight, which is used to enforce hierarchy and prevent privilege escalation.',
|
||||
'travelReimburse' => 'Travel Reimbursement',
|
||||
'office_travelreimburse_homework_company_name' => 'Default company for home-work',
|
||||
'office_travelreimburse_cents_homework_incl' => 'Cents per/km home-work incl. VAT',
|
||||
'office_travelreimburse_cents_homework_excl' => 'Cents per/km home-work excl. VAT',
|
||||
'office_travelreimburse_cents_business_incl' => 'Cents per/km business incl. VAT',
|
||||
'office_travelreimburse_cents_business_excl' => 'Cents per/km business excl. VAT',
|
||||
'total_travel_distance_homework' => 'Home-work',
|
||||
'total_travel_distance_business' => 'Business',
|
||||
'total_travel_incl' => 'Total incl. VAT',
|
||||
'total_travel_excl' => 'Total excl. VAT',
|
||||
'total' => 'Total',
|
||||
'departure_postcode' => 'Departure Postcode',
|
||||
'destination_postcode' => 'Destination Postcode',
|
||||
'kilometers' => 'Kilometers',
|
||||
'save_default' => 'Save defaults',
|
||||
'default_saved' => 'Defaults saved',
|
||||
'homework' => 'Home-work',
|
||||
'business' => 'Business',
|
||||
'default_have_been_saved' => 'Your default travel values have been saved.',
|
||||
'mail_travelreimburse' => 'Mail travel reimbursement',
|
||||
'invoice_number' => 'Invoice number',
|
||||
];
|
||||
@@ -39,6 +39,7 @@ return [
|
||||
'access_control' => 'Toegangs beheer',
|
||||
'monitoring' => 'Monitoring',
|
||||
'systemconfig' => 'Instellingen',
|
||||
'configure' => 'Configureer',
|
||||
'portal-management' => 'Portaal beheer',
|
||||
'dashboard_text_admin_management' => 'Toegang beheer voor administrators',
|
||||
'dashboard_text_access_control' => 'Beheer permissies en groepen',
|
||||
@@ -231,7 +232,7 @@ return [
|
||||
'autoproviosioning' => 'Autoprovisioning',
|
||||
'dashboard_text_stompjes' => 'Kan je stompen?',
|
||||
'office' => 'Kantoor',
|
||||
'stompjeslist' => 'Stompjeslijst',
|
||||
'stompjeslist' => 'Stompjes',
|
||||
'stompable' => 'Stompabel',
|
||||
'stompjes' => 'Stompjes',
|
||||
'stomped' => 'Gestompt',
|
||||
@@ -307,5 +308,27 @@ return [
|
||||
'source_inserve_sync_licenses_desc' => 'Deze API-call voert eerst de <b>Synchroniseer cloud-distributeurbedrijven</b> actie uit en synchroniseert daarna alle servers in een actieve, verwijderde of trial status met Inserve-licenties. Het maakt serverlicenties in Inserve aan of werkt ze bij als ze niet bestaan of als de licentieaantallen afwijken van die in Sentri.',
|
||||
'source_inserve_sync_subscriptions' => 'Synchroniseer serverabonnementen',
|
||||
'source_inserve_sync_subscriptions_desc' => 'Deze API-call voert eerst de actie <b>Sync cloud distributor companies</b> uit en maakt vervolgens een abonnement aan als dit nog niet bestaat, of werkt de beschrijving van het abonnement bij als het wel bestaat. Daarnaast worden abonnementsregels aangemaakt met de juiste product_code die is ingevuld in het <b>custom source data</b> JSON-veld.',
|
||||
'not_allowed_to_change_due_to_group_weight' => 'U mag deze instellingen niet wijzigen omdat deze een lagere groepsgewichtwaarde hebben, wat wordt gebruikt om de hiërarchie af te dwingen en privilege-escalatie te voorkomen.'
|
||||
'not_allowed_to_change_due_to_group_weight' => 'U mag deze instellingen niet wijzigen omdat deze een lagere groepsgewichtwaarde hebben, wat wordt gebruikt om de hiërarchie af te dwingen en privilege-escalatie te voorkomen.',
|
||||
'travelReimburse' => 'Reiskosten',
|
||||
'travelReimburse_overview' => 'Reiskostenoverzicht',
|
||||
'office_travelreimburse_homework_company_name' => 'Standaard woonwerk bedrijf',
|
||||
'office_travelreimburse_cents_homework_incl' => 'Cent per/km woon-werk incl. BTW',
|
||||
'office_travelreimburse_cents_homework_excl' => 'Cent per/km woon-werk excl. BTW',
|
||||
'office_travelreimburse_cents_business_incl' => 'Cent per/km zakelijk incl. BTW',
|
||||
'office_travelreimburse_cents_business_excl' => 'Cent per/km zakelijk excl. BTW',
|
||||
'total_travel_distance_homework' => 'Woonwerk',
|
||||
'total_travel_distance_business' => 'Zakelijk',
|
||||
'total_travel_incl' => 'Totaal incl. BTW',
|
||||
'total_travel_excl' => 'Totaal excl. BTW',
|
||||
'total' => 'Totaal',
|
||||
'departure_postcode' => 'Vertrek postcode',
|
||||
'destination' => 'Bestemming postcode',
|
||||
'kilometers' => 'Kilometers',
|
||||
'save_default' => 'Standaard opslaan',
|
||||
'default_saved' => 'Standaard opgeslagen',
|
||||
'homework' => 'Woon/werk',
|
||||
'business' => 'Zakelijk',
|
||||
'default_have_been_saved' => 'De standaard instellingen zijn opgeslagen.',
|
||||
'mail_travelreimburse' => 'Reiskosten mailen',
|
||||
'invoice_number' => 'Factuurnummer',
|
||||
];
|
||||
263
pub/bin/pages/office/pageTravelReimbursement.php
Normal file
263
pub/bin/pages/office/pageTravelReimbursement.php
Normal file
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
|
||||
use api\classes\API_office_travel_reimburse;
|
||||
use api\classes\API_companies;
|
||||
|
||||
if (!defined('APP_INIT')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!$GLOBALS['modules_enabled']['office']) {
|
||||
echo '405 Not Allowed';
|
||||
exit;
|
||||
}
|
||||
|
||||
# IDE Section
|
||||
|
||||
# Includes Section
|
||||
require_once "{$_SERVER['DOCUMENT_ROOT']}/api/classes/API_office_travel_reimburse.php";
|
||||
require_once "{$_SERVER['DOCUMENT_ROOT']}/api/classes/API_companies.php";
|
||||
|
||||
# Check permissions
|
||||
$API = new API_office_travel_reimburse();
|
||||
if (!$API->checkPermissions('office-travel-reimburse', 'RO', true)) {
|
||||
echo 'error 401 unauthorized';
|
||||
exit;
|
||||
}
|
||||
|
||||
# Page functions
|
||||
|
||||
# JS Scripts to load for this page
|
||||
$jsScriptLoadData['/src/js/plugin/fullcalendar/fullcalendar.global.js'] = true;
|
||||
$jsScriptLoadData['/src/css/fullcalendar/theme/global.js'] = true;
|
||||
$jsScriptLoadData['/src/js/sentri/travelReimburse.js'] = true;
|
||||
$jsScriptLoadData['/src/js/sentri/companiesMap.js'] = true;
|
||||
|
||||
# PageClasses Setup
|
||||
|
||||
# Retrieve Information for the page
|
||||
$API_companies = new API_companies();
|
||||
$_GET['builder'] = [1 => ['where' => [0 => 'company_state', 1 => 'active']]];
|
||||
|
||||
# Retrieve Information for the page
|
||||
$portal_settings = $GLOBALS['conn']->query("SELECT * FROM system_settings")->fetch_assoc();
|
||||
|
||||
$companies = $API_companies->getCompanies();
|
||||
|
||||
# Start page output
|
||||
?>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body pt-2">
|
||||
<form id="defaultForNew" enctype="multipart/form-data" method="post" action="/api/v1/portal-management/users/">
|
||||
<div class="row align-items-end mt-0">
|
||||
<div class="col mt-0">
|
||||
<label for="new_departure_postcode" class="form-label"><?php echo __('departure_postcode') ?></label>
|
||||
<input type="text" id="new_departure_postcode" class="form-control" value="" placeholder="Departure Postcode" required>
|
||||
</div>
|
||||
|
||||
<div class="col mt-0">
|
||||
<label for="new_destination_postcode" class="form-label"><?php echo __('destination_postcode') ?></label>
|
||||
<input type="text" id="new_destination_postcode" class="form-control" value="" placeholder="Destination Postcode" required>
|
||||
</div>
|
||||
|
||||
<div class="col mt-0">
|
||||
<label for="new_travel_distance" class="form-label"><?php echo __('kilometers') ?></label>
|
||||
<input type="number" id="new_travel_distance" class="form-control" value="" placeholder="0" required>
|
||||
</div>
|
||||
|
||||
<div class="col mt-0">
|
||||
<label for="new_office_travelreimburse_company_uuid" class="form-label"><?php echo __('company') ?></label>
|
||||
<select id="new_office_travelreimburse_company_uuid" class="form-select" required>
|
||||
<option value=""></option>
|
||||
<?php foreach ($companies as $company) { ?>
|
||||
<option value="<?php echo $company['company_uuid'] ?>">
|
||||
<?php echo $company['company_name'] ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="form-check-label" for="new_homework"><?php echo __('homework') ?></label>
|
||||
<div class="form-check form-switch pb-3 mb-4">
|
||||
<input class="form-check-input" type="checkbox" id="new_homework">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<?php echo __('save_default') ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid h-100 d-flex flex-column p-0">
|
||||
<div id="calendar" class="flex-grow-1"></div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="travelModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header">
|
||||
<h5><?php echo __('edit') . ' ' . __('travelReimburse') ?></h5>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="reimburse_uuid">
|
||||
<input type="hidden" id="travel_date">
|
||||
|
||||
<div class="mb-3">
|
||||
<label><?php echo __('departure_postcode') ?></label>
|
||||
<label for="departure_postcode"></label><input class="form-control" id="departure_postcode" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label><?php echo __('destination_postcode') ?></label>
|
||||
<label for="destination_postcode"></label><input class="form-control" id="destination_postcode" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label><?php echo __('kilometers') ?></label>
|
||||
<label for="travel_distance"></label><input type="number" class="form-control" id="travel_distance" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label><?php echo __('company') ?></label>
|
||||
<select id="office_travelreimburse_company_uuid" aria-label="office_travelreimburse_company_uuid" aria-describedby="office_travelreimburse_company_uuid" class="form-control" required>
|
||||
<option value=""></option>
|
||||
<?php foreach ($companies as $company) { ?>
|
||||
<option value="<?php echo $company['company_uuid'] ?>"><?php echo $company['company_name'] ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label><?php echo __('description') ?></label>
|
||||
<label for="travel_description"></label><textarea class="form-control" id="travel_description"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch pb-3 mb-3">
|
||||
<label class="form-check-label" for="homework"><?php echo __('total_travel_distance_homework') ?></label>
|
||||
<input class="form-check-input" type="checkbox" id="homework">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-danger" id="deleteTravelBtn"><?php echo __('delete') ?></button>
|
||||
<button class="btn btn-primary" id="saveTravelBtn"><?php echo __('save') ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<table class="table table-sm table-responsive">
|
||||
<tr>
|
||||
<td><?php echo __('office_travelreimburse_cents_homework_incl') ?>:</td>
|
||||
<td>
|
||||
<span id="office_travelreimburse_cents_homework_incl">
|
||||
€ <?php echo number_format($portal_settings['office_travelreimburse_cents_homework_incl'] / 100, 2, ',', '.') ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span id="total_travel_distance_homework_incl"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span id="office_travelreimburse_cents_homework_incl_total">
|
||||
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?php echo __('office_travelreimburse_cents_homework_excl') ?>:</td>
|
||||
<td>
|
||||
<span id="office_travelreimburse_cents_homework_excl">
|
||||
€ <?php echo number_format($portal_settings['office_travelreimburse_cents_homework_excl'] / 100, 2, ',', '.') ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span id="total_travel_distance_homework_excl"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span id="office_travelreimburse_cents_homework_excl_total">
|
||||
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?php echo __('office_travelreimburse_cents_business_incl') ?>:</td>
|
||||
<td>
|
||||
<span id="office_travelreimburse_cents_business_incl">
|
||||
€ <?php echo number_format($portal_settings['office_travelreimburse_cents_business_incl'] / 100, 2, ',', '.') ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span id="total_travel_distance_business_incl"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span id="office_travelreimburse_cents_business_incl_total">
|
||||
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?php echo __('office_travelreimburse_cents_business_excl') ?>:</td>
|
||||
<td>
|
||||
<span id="office_travelreimburse_cents_business_excl">
|
||||
€ <?php echo number_format($portal_settings['office_travelreimburse_cents_business_excl'] / 100, 2, ',', '.') ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span id="total_travel_distance_business_excl"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span id="office_travelreimburse_cents_business_excl_total">
|
||||
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?php echo __('total_travel_incl') ?>:</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>
|
||||
<span id="total_travel_incl">
|
||||
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?php echo __('total_travel_excl') ?>:</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>
|
||||
<span id="total_travel_excl">
|
||||
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<form method="POST" action="/api/v1/office/travel-reimburse/send/">
|
||||
<input type="hidden" name="calender_month" id="calender_month" value="">
|
||||
<button class="btn btn-success" type="submit">
|
||||
<i class="fa-regular fa-envelope"></i> <?php echo __('mail_travelreimburse') ?>
|
||||
<br> (<?php echo $_SESSION['user']['user_email'] ?>)
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -49,6 +49,7 @@ function showCard($module_name, $page_name): void
|
||||
|
||||
if ($GLOBALS['modules_enabled']['office'] && $API->checkPermissions('office-stompjes', 'RO', true)) {
|
||||
showCard('office', 'stompjeslist');
|
||||
showCard('office', 'travelReimburse');
|
||||
}
|
||||
|
||||
if ($GLOBALS['modules_enabled']['autop']) {
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
if (!defined('APP_INIT')) {
|
||||
exit;
|
||||
}
|
||||
# IDE Section
|
||||
|
||||
|
||||
# Includes Section
|
||||
|
||||
require_once "{$_SERVER['DOCUMENT_ROOT']}/api/classes/API_companies.php";
|
||||
# Check permissions
|
||||
$API = new \api\classes\API_companies();
|
||||
|
||||
# Page functions
|
||||
|
||||
|
||||
# JS Scripts to load for this page
|
||||
$jsScriptLoadData['/src/js/sentri/activeTabOnRefresh.js'] = true;
|
||||
$jsScriptLoadData['/src/js/sentri/copyInputValue.js'] = true;
|
||||
$jsScriptLoadData['/src/js/sentri/updateToggle.js'] = true;
|
||||
|
||||
# PageClasses Setup
|
||||
|
||||
# Retrieve Information for the page
|
||||
$portal_settings = $GLOBALS['conn']->query("SELECT * FROM system_settings")->fetch_assoc();
|
||||
|
||||
$companies = [];
|
||||
if ($GLOBALS['modules_enabled']['customers']) {
|
||||
$_GET['builder'] = [1 => ['where' => [0 => 'company_state', 1 => 'active']]];
|
||||
$companies = $API->getCompanies();
|
||||
}
|
||||
|
||||
# Set breadcrumb data
|
||||
|
||||
# Start page output
|
||||
|
||||
?><?php if ($API->checkPermissions('admin-portalsettings', 'RO', true)) { ?>
|
||||
<div class="card-body activeTabOnRefresh">
|
||||
<div class="row">
|
||||
<div class="col-md-1 col-lg-2">
|
||||
<div class="nav flex-column nav-pills nav-secondary nav-pills-no-bd nav-pills-icons" id="v-pills-tab-with-icon" role="tablist" aria-orientation="vertical">
|
||||
<a class="nav-link" id="travel-reimbursement-settings-tab" data-bs-toggle="pill" href="#travel-reimburse-settings" role="tab" aria-controls="mail-settings" aria-selected="true">
|
||||
<i class="<?php echo $GLOBALS['pages']['office']['travelReimburse']['page_icon'] ?>"></i><?php echo __('travelReimburse'); ?>
|
||||
</a>
|
||||
|
||||
<a class="nav-link" id="global-settings-tab" data-bs-toggle="pill" href="#stompjes-settings" role="tab" aria-controls="global-settings">
|
||||
<i class="<?php echo $GLOBALS['pages']['office']['stompjeslist']['page_icon'] ?>"></i><?php echo __('stompjeslist'); ?>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-11 col-lg-10">
|
||||
<div class="tab-content" id="v-pills-with-icon-tabContent">
|
||||
<div class="tab-pane fade show active" id="travel-reimburse-settings" role="tabpanel" aria-labelledby="travel-reimbursement-settings-tab">
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<h2>
|
||||
<i class="fas fa-globe-americas"></i> <?php echo __('travelReimburse') . " " . __('settings'); ?>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<form id="FormValidation" method="post" action="/api/v1/office/travel-reimburse/configure/">
|
||||
<input type="hidden" name="X-HTTP-Method-Override" value="PUT">
|
||||
<input type="hidden" name="_return" value="/portal-management/modules/office/#travel-reimburse-settings">
|
||||
<input type="hidden" name="portal_uuid" value="<?php echo $portal_settings['portal_uuid']; ?>">
|
||||
<div class="card-body">
|
||||
<div class="form-group form-show-validation row">
|
||||
<label for="office_travelreimburse_cents_homework_incl" class="col-lg-4 col-md-4 col-sm-4 mt-sm-2"><?php echo __('office_travelreimburse_cents_homework_incl') ?></label>
|
||||
<div class="col-lg-3 col-md-3 col-sm-3">
|
||||
<input type="number" class="form-control" id="office_travelreimburse_cents_homework_incl" name="office_travelreimburse_cents_homework_incl" value="<?php echo $portal_settings['office_travelreimburse_cents_homework_incl'] ?>" placeholder="" required/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group form-show-validation row">
|
||||
<label for="office_travelreimburse_cents_homework_excl" class="col-lg-4 col-md-4 col-sm-4 mt-sm-2"><?php echo __('office_travelreimburse_cents_homework_excl') ?></label>
|
||||
<div class="col-lg-3 col-md-3 col-sm-3">
|
||||
<input type="number" class="form-control" id="office_travelreimburse_cents_homework_excl" name="office_travelreimburse_cents_homework_excl" value="<?php echo $portal_settings['office_travelreimburse_cents_homework_excl'] ?>" placeholder="" required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group form-show-validation row">
|
||||
<label for="office_travelreimburse_cents_business_incl" class="col-lg-4 col-md-4 col-sm-4 mt-sm-2"><?php echo __('office_travelreimburse_cents_business_incl') ?></label>
|
||||
<div class="col-lg-3 col-md-3 col-sm-3">
|
||||
<input type="number" class="form-control" id="office_travelreimburse_cents_business_incl" name="office_travelreimburse_cents_business_incl" value="<?php echo $portal_settings['office_travelreimburse_cents_business_incl'] ?>" placeholder="" required/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group form-show-validation row">
|
||||
<label for="office_travelreimburse_cents_business_excl" class="col-lg-4 col-md-4 col-sm-4 mt-sm-2"><?php echo __('office_travelreimburse_cents_business_excl') ?></label>
|
||||
<div class="col-lg-3 col-md-3 col-sm-3">
|
||||
<input type="number" class="form-control" id="office_travelreimburse_cents_business_excl" name="office_travelreimburse_cents_business_excl" value="<?php echo $portal_settings['office_travelreimburse_cents_business_excl'] ?>" placeholder="" required/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer py-4">
|
||||
<div class="row">
|
||||
<div class="col d-flex justify-content-end">
|
||||
<?php if ($API->checkPermissions('admin-portalsettings', 'RW', true)) { ?>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-edit"></i> <?php echo __('edit') ?>
|
||||
</button>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade show active" id="stompjes-settings" role="tabpanel" aria-labelledby="stompjes-settings-tab">
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<h2>
|
||||
<i class="fa-solid fa-envelope"></i> <?php echo __('stompjes') . ' ' . __('settings') ?>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
No settings for stompjes yet.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
@@ -319,12 +319,14 @@ while ($module = $system_modules_data->fetch_assoc()) {
|
||||
<tr>
|
||||
<th><?php echo __('module_name') ?></th>
|
||||
<th><?php echo __('enabled') ?></th>
|
||||
<th><?php echo __('configure') ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th><?php echo __('module_name') ?></th>
|
||||
<th><?php echo __('enabled') ?></th>
|
||||
<th><?php echo __('configure') ?></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
@@ -342,6 +344,11 @@ while ($module = $system_modules_data->fetch_assoc()) {
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</td>
|
||||
<td class="text-nowrap">
|
||||
<?php if ($module['module_slugify'] === 'office') { ?>
|
||||
<a href="/portal-management/modules/office/" class="btn btn-primary btn-sm btn-rounded"><i class="fa-solid fa-gear"></i></a>
|
||||
<?php } ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
|
||||
@@ -10,6 +10,7 @@ require_once "{$_SERVER['DOCUMENT_ROOT']}/../vendor/autoload.php";
|
||||
class mailBuilder
|
||||
{
|
||||
public PHPMailer $mail;
|
||||
|
||||
public string $subject;
|
||||
public string $mailText;
|
||||
|
||||
@@ -60,6 +61,14 @@ class mailBuilder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
function addAttachment($path, $name): void
|
||||
{
|
||||
$this->mail->addAttachment($path, $name);
|
||||
}
|
||||
|
||||
function sendMail(): bool
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -113,7 +113,9 @@ class pageBuilder
|
||||
<link rel="stylesheet" href="/src/css/sentri.min.css"/> <!-- Special css tricks for Sentri -->
|
||||
<link rel="stylesheet" href="/src/css/plugins.min.css"/> <!-- need this for scroll bars and other small things -->
|
||||
<link rel="stylesheet" href="/src/js/plugin/sweetalert2/sweetalert2.min.css"/> <!-- need this for scroll bars and other small things -->
|
||||
|
||||
<link rel="stylesheet" href="/src/css/fullcalendar/skeleton.css"/> <!-- Used for fullcalander.js -->
|
||||
<link rel="stylesheet" href="/src/css/fullcalendar/theme/theme.css"/> <!-- Used for fullcalander.js -->
|
||||
<link rel="stylesheet" href="/src/css/fullcalendar/theme/palettes/purple.css"/> <!-- Used for fullcalander.js -->
|
||||
|
||||
<!--<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/lipis/flag-icons@7.2.3/css/flag-icons.min.css"/> -->
|
||||
|
||||
@@ -184,6 +186,7 @@ class pageBuilder
|
||||
if ($GLOBALS['modules_enabled']['office'] && $API->checkPermissions('office-stompjes', 'RO', true)) {
|
||||
showSpan('office');
|
||||
showPage('office', 'stompjeslist');
|
||||
showPage('office', 'travelReimburse');
|
||||
}
|
||||
|
||||
if ($GLOBALS['modules_enabled']['servers'] && $API->checkPermissions('servers', 'RO', true)) {
|
||||
|
||||
489
pub/src/css/fullcalendar/skeleton.css
vendored
Normal file
489
pub/src/css/fullcalendar/skeleton.css
vendored
Normal file
@@ -0,0 +1,489 @@
|
||||
|
||||
:root {
|
||||
--fc-sticky-header-footer-z: 3;
|
||||
--fc-popover-z: 4;
|
||||
}
|
||||
|
||||
.fc-oH {
|
||||
z-index: var(--fc-popover-z) !important;
|
||||
}
|
||||
|
||||
.fc-XR {
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
/*
|
||||
This will prevent all ancestors from customizing their box size unless they use .contentBox
|
||||
*/
|
||||
.fc-7V,
|
||||
.fc-7V *,
|
||||
.fc-7V *:before,
|
||||
.fc-7V *:after {
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
/* classes attached to <body> */
|
||||
.fc-Ph,
|
||||
.fc-Ph * {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
.fc-ia {
|
||||
-ms-overflow-style: none !important; /* IE and Edge */
|
||||
scrollbar-width: none !important; /* Firefox */
|
||||
}
|
||||
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
.fc-ia::-webkit-scrollbar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.fc-zx {
|
||||
flex-shrink: 0 !important;
|
||||
}
|
||||
|
||||
.fc-Qv .fc-zg {
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
flex-wrap: wrap !important;
|
||||
}
|
||||
|
||||
/* HACK for Safari. Can't do break-inside:avoid with flexbox items, likely b/c it's not standard:
|
||||
https://stackoverflow.com/a/60256345 */
|
||||
.fc-SB .fc-zg > * {
|
||||
float: left !important;
|
||||
}
|
||||
|
||||
[dir=rtl] .fc-SB .fc-zg > *,
|
||||
.fc-SB[dir=rtl] .fc-zg > * {
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
.fc-SB .fc-zg::after {
|
||||
content: "" !important;
|
||||
display: block !important;
|
||||
clear: both !important;
|
||||
}
|
||||
|
||||
.fc-oq {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.fc-7Z {
|
||||
cursor: n-resize !important;
|
||||
}
|
||||
|
||||
.fc-qE {
|
||||
cursor: s-resize !important;
|
||||
}
|
||||
|
||||
.fc-FE {
|
||||
cursor: w-resize !important;
|
||||
}
|
||||
|
||||
[dir=rtl] .fc-FE {
|
||||
cursor: e-resize !important;
|
||||
}
|
||||
|
||||
.fc-cf {
|
||||
cursor: e-resize !important;
|
||||
}
|
||||
|
||||
[dir=rtl] .fc-cf {
|
||||
cursor: w-resize !important;
|
||||
}
|
||||
|
||||
.fc-zd {
|
||||
cursor: col-resize !important;
|
||||
}
|
||||
|
||||
.fc-OF,
|
||||
.fc-Vs,
|
||||
.fc-vB {
|
||||
position: absolute !important;
|
||||
box-sizing: content-box !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.fc-OF {
|
||||
padding: 10px !important;
|
||||
margin: -10px !important;
|
||||
}
|
||||
|
||||
.fc-Vs {
|
||||
padding-left: 10px !important;
|
||||
padding-right: 10px !important;
|
||||
margin-left: -10px !important;
|
||||
margin-right: -10px !important;
|
||||
}
|
||||
|
||||
.fc-vB {
|
||||
padding-top: 10px !important;
|
||||
padding-bottom: 10px !important;
|
||||
margin-top: -10px !important;
|
||||
margin-bottom: -10px !important;
|
||||
}
|
||||
|
||||
/* QUESTION: why not use -left -right instead of negative margins/padding for ALL? */
|
||||
.fc-Za {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: -5px;
|
||||
right: -5px;
|
||||
}
|
||||
|
||||
.fc-5b {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.fc-Ok {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* Border Utils */
|
||||
/* ------------------------------------------------------------------------------------------------- */
|
||||
|
||||
/* TODO: break-out to x and y. don't have "only" all */
|
||||
/* TODO: instead of "border" utils, call them "borderless" to subtract? */
|
||||
|
||||
.fc-4g {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.fc-k2 {
|
||||
border-left: 0 !important;
|
||||
border-right: 0 !important;
|
||||
border-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.fc-5H {
|
||||
border-top: 0 !important;
|
||||
border-left: 0 !important;
|
||||
border-right: 0 !important;
|
||||
}
|
||||
|
||||
.fc-eu,
|
||||
.fc-Cu {
|
||||
border-top: 0 !important;
|
||||
border-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.fc-eu {
|
||||
border-inline-end: 0 !important;
|
||||
}
|
||||
|
||||
.fc-Cu {
|
||||
border-inline-start: 0 !important;
|
||||
}
|
||||
|
||||
.fc-k0 {
|
||||
border-left: 0 !important;
|
||||
border-right: 0 !important;
|
||||
}
|
||||
|
||||
.fc-5s {
|
||||
border-top: 0 !important;
|
||||
border-bottom: 0 !important;
|
||||
}
|
||||
|
||||
/* for matching cell start-border, assumed to be 1px, which can't be guaranteed */
|
||||
.fc-qp {
|
||||
border-inline-start: 1px solid transparent !important;
|
||||
}
|
||||
|
||||
/* Flexbox Utils */
|
||||
/* ------------------------------------------------------------------------------------------------- */
|
||||
|
||||
.fc-iE {
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
}
|
||||
|
||||
.fc-Si {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
.fc-lf {
|
||||
flex-grow: 1 !important;
|
||||
}
|
||||
|
||||
.fc-EI {
|
||||
flex-grow: 1 !important;
|
||||
flex-basis: 0 !important;
|
||||
min-width: 0 !important;
|
||||
min-height: 0 !important;
|
||||
}
|
||||
|
||||
.fc-At {
|
||||
min-height: 0 !important;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: use liquidX/Y elsewhere because frees up min other dimension; less collisions
|
||||
*/
|
||||
.fc-4T {
|
||||
flex-grow: 1 !important;
|
||||
flex-basis: 0 !important;
|
||||
min-width: 0 !important;
|
||||
}
|
||||
|
||||
/* Print-Safe Utils (media:screen ONLY) */
|
||||
/* ------------------------------------------------------------------------------------------------- */
|
||||
|
||||
.fc-Qv .fc-E1,
|
||||
.fc-Qv .fc-r7 {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
/* Table Utils */
|
||||
/* ------------------------------------------------------------------------------------------------- */
|
||||
|
||||
.fc-p2 {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.fc-9j {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.fc-gE {
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.fc-zo {
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.fc-xd {
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
.fc-zK {
|
||||
white-space: pre !important;
|
||||
}
|
||||
|
||||
/* Misc Utils */
|
||||
/* ------------------------------------------------------------------------------------------------- */
|
||||
|
||||
.fc-4c {
|
||||
overflow-anchor: none;
|
||||
}
|
||||
|
||||
.fc-d5 {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: eventually use this on daygrid/timegrid events' inner, time, and title, FOR PRINT
|
||||
Needed to prevent wrapping to multiple lines, increasing height, throwing off print-positioning,
|
||||
which can't rely on dynamic height detection after print-flag activated.
|
||||
*/
|
||||
.fc-lN {
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.fc-RP {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.fc-d7 {
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
.fc-mj {
|
||||
inset-inline-start: 0 !important;
|
||||
}
|
||||
|
||||
.fc-7z {
|
||||
position: absolute !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
}
|
||||
|
||||
.fc-88,
|
||||
.fc-cb {
|
||||
position: absolute !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
}
|
||||
|
||||
.fc-PG,
|
||||
.fc-6E {
|
||||
position: absolute !important;
|
||||
top: 0 !important;
|
||||
bottom: 0 !important;
|
||||
}
|
||||
|
||||
.fc-88 {
|
||||
top: 0 !important;
|
||||
}
|
||||
|
||||
.fc-6E {
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
width: 0 !important;
|
||||
}
|
||||
|
||||
@media not print {
|
||||
.fc-Zx {
|
||||
position: sticky !important;
|
||||
}
|
||||
|
||||
.fc-vZ {
|
||||
position: sticky !important;
|
||||
top: 0 !important;
|
||||
}
|
||||
|
||||
/* Sticks to either left or right (the flex "start") depending on ltr/rtl */
|
||||
.fc-ry {
|
||||
position: sticky !important;
|
||||
inset-inline-start: 0 !important;
|
||||
}
|
||||
|
||||
.fc-Uy {
|
||||
position: sticky !important;
|
||||
top: 0 !important;
|
||||
z-index: var(--fc-sticky-header-footer-z) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only needed when padding/border must be separate from width/height */
|
||||
.fc-Pv {
|
||||
box-sizing: content-box !important;
|
||||
}
|
||||
|
||||
.fc-E4 {
|
||||
position: absolute !important;
|
||||
left: -10000px !important;
|
||||
}
|
||||
|
||||
.fc-dV {
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.fc-Zt {
|
||||
align-items: flex-start !important;
|
||||
}
|
||||
|
||||
.fc-fP {
|
||||
align-items: flex-end !important;
|
||||
}
|
||||
|
||||
/* Footer Scrollbar */
|
||||
/* ------------------------------------------------------------------------------------------------- */
|
||||
|
||||
.fc-sm {
|
||||
position: sticky !important;
|
||||
bottom: 0 !important;
|
||||
z-index: var(--fc-sticky-header-footer-z) !important;
|
||||
}
|
||||
|
||||
.fc-gr > * {
|
||||
margin-top: -1px !important;
|
||||
}
|
||||
|
||||
.fc-gr > * > * {
|
||||
height: 1px !important;
|
||||
}
|
||||
|
||||
/* Print-Safe Utils */
|
||||
/* ------------------------------------------------------------------------------------------------- */
|
||||
|
||||
.fc-SB .fc-V4 {
|
||||
break-inside: avoid !important;
|
||||
}
|
||||
|
||||
.fc-SB .fc-E1 {
|
||||
display: table !important;
|
||||
table-layout: fixed !important;
|
||||
width: 100% !important;
|
||||
border-spacing: 0 !important;
|
||||
border-collapse: separate !important;
|
||||
}
|
||||
|
||||
.fc-SB .fc-r7 {
|
||||
display: table-header-group !important;
|
||||
break-inside: avoid !important;
|
||||
background: #fff !important;
|
||||
z-index: 9999 !important;
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.fc-uo {
|
||||
/* min-height when multiple rows coexist, provides sane aspect-ratio for cells */
|
||||
min-height: 6em !important;
|
||||
}
|
||||
|
||||
/* Z-index */
|
||||
/* ------------------------------------------------------------------------------------------------- */
|
||||
|
||||
.fc-CX {
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.fc-ts {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.fc-cW:focus-visible {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* INTERNAL MARKER -- used to mark an element for internal significance */
|
||||
/* ------------------------------------------------------------------------------------------------- */
|
||||
|
||||
.fc-AW {
|
||||
}
|
||||
|
||||
.fc-So {
|
||||
}
|
||||
|
||||
.fc-Mr {
|
||||
}
|
||||
|
||||
.fc-y7 {
|
||||
}
|
||||
|
||||
.fc-eG {
|
||||
}
|
||||
|
||||
.fc-Mb {
|
||||
}
|
||||
|
||||
.fc-9u {
|
||||
}
|
||||
|
||||
.fc-BY {
|
||||
}
|
||||
|
||||
.fc-iD {
|
||||
}
|
||||
|
||||
.fc-GL {
|
||||
}
|
||||
|
||||
.fc-QC {
|
||||
}
|
||||
|
||||
.fc-hY {
|
||||
}
|
||||
|
||||
.fc-2y {
|
||||
}
|
||||
|
||||
.fc-kO {
|
||||
}
|
||||
|
||||
.fc-Pz {
|
||||
}
|
||||
387
pub/src/css/fullcalendar/theme/global.js
Normal file
387
pub/src/css/fullcalendar/theme/global.js
Normal file
@@ -0,0 +1,387 @@
|
||||
/*!
|
||||
FullCalendar (Vanilla JS) v7.0.0
|
||||
Docs & License: https://fullcalendar.io
|
||||
(c) 2026 Adam Shaw
|
||||
*/
|
||||
(function ({ H: joinClassNames, u, S, G: globalPlugins }) {
|
||||
|
||||
|
||||
// usually 11px font / 12px line-height
|
||||
const xxsTextClass = "fc-monarch-vQz";
|
||||
// outline
|
||||
const outlineWidthClass = "fc-monarch-x7S";
|
||||
const outlineWidthFocusClass = "fc-monarch-sOR";
|
||||
const outlineWidthGroupFocusClass = "fc-monarch-a9l";
|
||||
const outlineColorClass = "fc-monarch-MJY";
|
||||
const outlineFocusClass = `${outlineColorClass} ${outlineWidthFocusClass}`;
|
||||
// neutral buttons
|
||||
const strongSolidPressableClass = joinClassNames("fc-monarch-9LE", "fc-monarch-r63", "fc-monarch-ljX");
|
||||
const mutedHoverClass = "fc-monarch-v08";
|
||||
const mutedHoverGroupClass = "fc-monarch-JQh";
|
||||
const mutedHoverPressableClass = `${mutedHoverClass} fc-monarch-Yib fc-monarch-6MP`;
|
||||
const mutedHoverPressableGroupClass = `${mutedHoverGroupClass} fc-monarch-4Pp fc-monarch-Hsn`;
|
||||
// controls
|
||||
const selectedClass = "fc-monarch-y37 fc-monarch-bIt";
|
||||
const selectedPressableClasss = `${selectedClass} fc-monarch-SJz fc-monarch-MQC`;
|
||||
const selectedButtonClass = `${selectedPressableClasss} fc-monarch-wsy fc-monarch-d0j ${outlineFocusClass} fc-monarch-07j`;
|
||||
const unselectedButtonClass = `${mutedHoverPressableClass} fc-monarch-wsy fc-monarch-d0j ${outlineFocusClass} fc-monarch-07j`;
|
||||
// primary
|
||||
const primaryClass = "fc-monarch-zue fc-monarch-8ys";
|
||||
const primaryPressableClass = `${primaryClass} fc-monarch-4bi fc-monarch-yEk`;
|
||||
const primaryButtonClass = `${primaryPressableClass} fc-monarch-wsy fc-monarch-d0j ${outlineFocusClass}`;
|
||||
// secondary *calendar content* (has muted color)
|
||||
const secondaryClass = "fc-monarch-XMK fc-monarch-c3e";
|
||||
const secondaryPressableClass = `${secondaryClass} fc-monarch-ISM fc-monarch-KwH ${outlineFocusClass}`;
|
||||
// secondary *toolbar button* (neutral)
|
||||
const secondaryButtonClass = `${mutedHoverPressableClass} fc-monarch-wsy fc-monarch-zOd ${outlineFocusClass} fc-monarch-07j`;
|
||||
const secondaryButtonIconClass = `fc-monarch-XUJ fc-monarch-PVh fc-monarch-zn9 fc-monarch-Bpp`;
|
||||
// tertiary
|
||||
const tertiaryClass = "fc-monarch-4os fc-monarch-CCH";
|
||||
const tertiaryPressableClass = `${tertiaryClass} fc-monarch-hbI fc-monarch-yo2`;
|
||||
const tertiaryPressableGroupClass = `${tertiaryClass} fc-monarch-Yws fc-monarch-jc4 ${outlineFocusClass}`;
|
||||
// interactive neutral foregrounds
|
||||
const mutedFgPressableGroupClass = "fc-monarch-JMv fc-monarch-rih fc-monarch-8El";
|
||||
// transparent resizer for mouse
|
||||
const blockPointerResizerClass = "fc-monarch-1EY fc-monarch-pps fc-monarch-vs6";
|
||||
const rowPointerResizerClass = `${blockPointerResizerClass} fc-monarch-AWB fc-monarch-hza`;
|
||||
const columnPointerResizerClass = `${blockPointerResizerClass} fc-monarch-MaV fc-monarch-uuA`;
|
||||
// circle resizer for touch
|
||||
const blockTouchResizerClass = "fc-monarch-1EY fc-monarch-3wQ fc-monarch-wsy fc-monarch-lNM fc-monarch-MAH fc-monarch-AAA";
|
||||
const rowTouchResizerClass = `${blockTouchResizerClass} fc-monarch-ERR fc-monarch-Dq8`;
|
||||
const columnTouchResizerClass = `${blockTouchResizerClass} fc-monarch-1V6 fc-monarch-F99`;
|
||||
const tallDayCellBottomClass = "fc-monarch-jgW";
|
||||
const getShortDayCellBottomClass = (info) => joinClassNames(!info.isNarrow && "fc-monarch-toR");
|
||||
const dayRowCommonClasses = {
|
||||
/* Day Row > List-Item Event
|
||||
----------------------------------------------------------------------------------------------- */
|
||||
listItemEventClass: (info) => joinClassNames("fc-monarch-Ika fc-monarch-7A6 fc-monarch-Fvv", info.isNarrow ? "fc-monarch-148" : "fc-monarch-cKZ"),
|
||||
listItemEventBeforeClass: (info) => joinClassNames("fc-monarch-5JF", info.isNarrow ? "fc-monarch-Jzj" : "fc-monarch-Wga"),
|
||||
listItemEventInnerClass: (info) => (info.isNarrow
|
||||
? `fc-monarch-z5u ${xxsTextClass}`
|
||||
: "fc-monarch-2rx fc-monarch-a3B"),
|
||||
listItemEventTimeClass: (info) => joinClassNames(info.isNarrow ? "fc-monarch-a7i" : "fc-monarch-C2j", "fc-monarch-TZ4 fc-monarch-pKG fc-monarch-1Zl"),
|
||||
listItemEventTitleClass: (info) => joinClassNames(info.isNarrow ? "fc-monarch-oQ2" : "fc-monarch-aCI", "fc-monarch-DIS fc-monarch-TZ4 fc-monarch-pKG fc-monarch-OLq"),
|
||||
/* Day Row > Row Event
|
||||
----------------------------------------------------------------------------------------------- */
|
||||
rowEventClass: (info) => joinClassNames(info.isStart && "fc-monarch-qvL", info.isEnd && "fc-monarch-9hC"),
|
||||
rowEventInnerClass: (info) => info.isNarrow ? "fc-monarch-z5u" : "fc-monarch-2rx",
|
||||
/* Day Row > More-Link
|
||||
----------------------------------------------------------------------------------------------- */
|
||||
rowMoreLinkClass: (info) => joinClassNames("fc-monarch-Ika fc-monarch-wsy fc-monarch-Fvv", info.isNarrow
|
||||
? "fc-monarch-148 fc-monarch-yF0"
|
||||
: "fc-monarch-cKZ fc-monarch-d0j", mutedHoverPressableClass),
|
||||
rowMoreLinkInnerClass: (info) => (info.isNarrow
|
||||
? `fc-monarch-oQ2 fc-monarch-z5u ${xxsTextClass}`
|
||||
: "fc-monarch-aCI fc-monarch-2rx fc-monarch-a3B"),
|
||||
};
|
||||
const resourceDayHeaderClasses = {
|
||||
dayHeaderInnerClass: "fc-monarch-vUo",
|
||||
dayHeaderDividerClass: "fc-monarch-zi1 fc-monarch-jdl",
|
||||
};
|
||||
var index = {
|
||||
name: "theme-monarch",
|
||||
optionDefaults: {
|
||||
className: "fc-monarch-PVh fc-monarch-n5m",
|
||||
viewClass: (info) => {
|
||||
const hasBorderTop = !info.options.headerToolbar && !info.borderlessTop;
|
||||
const hasBorderBottom = !info.options.footerToolbar && !info.borderlessBottom;
|
||||
const hasBorderX = !info.borderlessX;
|
||||
return joinClassNames("fc-monarch-MAH fc-monarch-jdl", hasBorderTop && "fc-monarch-ku3", hasBorderBottom && "fc-monarch-zi1", hasBorderX && "fc-monarch-1Wx", (hasBorderTop && hasBorderX) && "fc-monarch-9hN", (hasBorderBottom && hasBorderX) && "fc-monarch-KxZ", !info.isHeightAuto && "fc-monarch-pKG");
|
||||
},
|
||||
/* Toolbar
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
toolbarClass: (info) => joinClassNames("fc-monarch-lqx fc-monarch-dl1 fc-monarch-1sP fc-monarch-dNl fc-monarch-XpK fc-monarch-N2M fc-monarch-wwb", "fc-monarch-MAH fc-monarch-jdl", !info.borderlessX && "fc-monarch-1Wx"),
|
||||
headerToolbarClass: (info) => joinClassNames(!info.borderlessTop && "fc-monarch-ku3", !(info.borderlessTop || info.borderlessX) && "fc-monarch-9hN"),
|
||||
footerToolbarClass: (info) => joinClassNames(!info.borderlessBottom && "fc-monarch-zi1", !(info.borderlessBottom || info.borderlessX) && "fc-monarch-KxZ"),
|
||||
toolbarSectionClass: "fc-monarch-yi0 fc-monarch-dl1 fc-monarch-1sP fc-monarch-XpK fc-monarch-wwb",
|
||||
toolbarTitleClass: "fc-monarch-AVD fc-monarch-DIS",
|
||||
buttonGroupClass: (info) => joinClassNames("fc-monarch-AAA fc-monarch-dl1 fc-monarch-1sP fc-monarch-XpK", info.hasSelection && "fc-monarch-wsy fc-monarch-jdl"),
|
||||
buttonClass: (info) => joinClassNames("fc-monarch-8cT fc-monarch-AAA fc-monarch-dl1 fc-monarch-1sP fc-monarch-XpK fc-monarch-9yp fc-monarch-Z9U", info.isIconOnly ? "fc-monarch-Eaq" : "fc-monarch-5su", info.buttonGroup?.hasSelection && "fc-monarch-CH7", (info.isIconOnly || (info.buttonGroup?.hasSelection && !info.isSelected))
|
||||
? unselectedButtonClass
|
||||
: info.isSelected
|
||||
? selectedButtonClass
|
||||
: info.isPrimary
|
||||
? primaryButtonClass
|
||||
: secondaryButtonClass),
|
||||
buttons: {
|
||||
prev: {
|
||||
iconContent: () => chevronDown(joinClassNames(secondaryButtonIconClass, "fc-monarch-z44 fc-monarch-keW"))
|
||||
},
|
||||
next: {
|
||||
iconContent: () => chevronDown(joinClassNames(secondaryButtonIconClass, "fc-monarch-KxI fc-monarch-ZW3"))
|
||||
},
|
||||
prevYear: {
|
||||
iconContent: () => chevronDoubleLeft(joinClassNames(secondaryButtonIconClass, "fc-monarch-asP"))
|
||||
},
|
||||
nextYear: {
|
||||
iconContent: () => chevronDoubleLeft(joinClassNames(secondaryButtonIconClass, "fc-monarch-jmT fc-monarch-jY6"))
|
||||
},
|
||||
},
|
||||
/* Abstract Event
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
eventShortHeight: 50,
|
||||
eventColor: "var(--fc-monarch-event)",
|
||||
eventContrastColor: "var(--fc-monarch-event-contrast)",
|
||||
eventClass: (info) => joinClassNames(info.isDragging && "fc-monarch-n5m", info.event.url && "fc-monarch-JiE", info.isSelected
|
||||
? joinClassNames(outlineWidthClass, info.isDragging ? "fc-monarch-1kP" : "fc-monarch-tkw")
|
||||
: outlineWidthFocusClass, outlineColorClass),
|
||||
/* Background Event
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
backgroundEventColor: "var(--fc-monarch-tertiary)",
|
||||
backgroundEventClass: "fc-monarch-gTC fc-monarch-jsy fc-monarch-DO7",
|
||||
backgroundEventTitleClass: (info) => joinClassNames("fc-monarch-lMo fc-monarch-L1Y", info.isNarrow
|
||||
? `fc-monarch-aCI fc-monarch-End ${xxsTextClass}`
|
||||
: "fc-monarch-Nca fc-monarch-8cT fc-monarch-a3B"),
|
||||
/* List-Item Event
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
listItemEventClass: (info) => joinClassNames("fc-monarch-XpK", info.isSelected
|
||||
? "fc-monarch-4Xm"
|
||||
: info.isInteractive
|
||||
? mutedHoverPressableClass
|
||||
: mutedHoverClass),
|
||||
listItemEventBeforeClass: "fc-monarch-AAA fc-monarch-lNM",
|
||||
listItemEventInnerClass: "fc-monarch-PVh fc-monarch-dl1 fc-monarch-1sP fc-monarch-XpK",
|
||||
/* Block Event
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
blockEventClass: (info) => joinClassNames("fc-monarch-bCs fc-monarch-eYX fc-monarch-d0j fc-monarch-DO7 fc-monarch-YjJ fc-monarch-Frw fc-monarch-vwH", info.isInteractive && "fc-monarch-st8", (!info.isSelected && info.isDragging) && "fc-monarch-iTG"),
|
||||
blockEventInnerClass: "fc-monarch-i9F fc-monarch-cfp",
|
||||
blockEventTimeClass: "fc-monarch-TZ4 fc-monarch-pKG",
|
||||
blockEventTitleClass: "fc-monarch-TZ4 fc-monarch-pKG",
|
||||
/* Row Event
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
rowEventClass: (info) => joinClassNames("fc-monarch-Ika fc-monarch-JIC", info.isStart ? "fc-monarch-3J4 fc-monarch-kmj" : (!info.isNarrow && "fc-monarch-Mde"), info.isEnd ? "fc-monarch-USt fc-monarch-Skl" : (!info.isNarrow && "fc-monarch-FvP")),
|
||||
rowEventBeforeClass: (info) => joinClassNames(info.isStartResizable ? joinClassNames(info.isSelected ? rowTouchResizerClass : rowPointerResizerClass, "fc-monarch-11a") : (!info.isStart && !info.isNarrow) && "fc-monarch-1EY fc-monarch-0fm fc-monarch-hza fc-monarch-DGM fc-monarch-Uvq"),
|
||||
rowEventBeforeContent: (info) => ((!info.isStart && !info.isNarrow) ? filledRightTriangle("fc-monarch-Pwv fc-monarch-jmT fc-monarch-jY6 fc-monarch-Mra") : u(S, {})),
|
||||
rowEventAfterClass: (info) => joinClassNames(info.isEndResizable ? joinClassNames(info.isSelected ? rowTouchResizerClass : rowPointerResizerClass, "fc-monarch-Tuc") : (!info.isEnd && !info.isNarrow) && "fc-monarch-1EY fc-monarch-eDA fc-monarch-hza fc-monarch-DGM fc-monarch-Uvq"),
|
||||
rowEventAfterContent: (info) => ((!info.isEnd && !info.isNarrow) ? filledRightTriangle("fc-monarch-Pwv fc-monarch-asP fc-monarch-Mra") : u(S, {})),
|
||||
rowEventInnerClass: (info) => joinClassNames("fc-monarch-dl1 fc-monarch-1sP fc-monarch-XpK", info.isNarrow ? xxsTextClass : "fc-monarch-a3B"),
|
||||
rowEventTimeClass: (info) => joinClassNames("fc-monarch-DIS fc-monarch-1Zl", info.isNarrow ? "fc-monarch-a7i" : "fc-monarch-C2j"),
|
||||
rowEventTitleClass: (info) => joinClassNames("fc-monarch-OLq", info.isNarrow ? "fc-monarch-oQ2" : "fc-monarch-aCI"),
|
||||
/* Column Event
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
columnEventTitleSticky: false,
|
||||
columnEventClass: (info) => joinClassNames(`fc-monarch-1Wx fc-monarch-A3h fc-monarch-SC5`, info.isStart && "fc-monarch-ku3 fc-monarch-Z7Q", info.isEnd && "fc-monarch-Ika fc-monarch-zi1 fc-monarch-2qh"),
|
||||
columnEventBeforeClass: (info) => joinClassNames(info.isStartResizable && joinClassNames(info.isSelected ? columnTouchResizerClass : columnPointerResizerClass, "fc-monarch-YDC")),
|
||||
columnEventAfterClass: (info) => joinClassNames(info.isEndResizable && joinClassNames(info.isSelected ? columnTouchResizerClass : columnPointerResizerClass, "fc-monarch-fJL")),
|
||||
columnEventInnerClass: (info) => joinClassNames("fc-monarch-dl1", info.isShort
|
||||
? "fc-monarch-1sP fc-monarch-XpK fc-monarch-iS4 fc-monarch-NWN"
|
||||
: joinClassNames("fc-monarch-sgX", info.isNarrow ? "fc-monarch-aCI fc-monarch-2rx" : "fc-monarch-Nca fc-monarch-Jhn"), (info.isShort || info.isNarrow) ? xxsTextClass : "fc-monarch-a3B"),
|
||||
columnEventTimeClass: (info) => joinClassNames("fc-monarch-NPw fc-monarch-OLq", !info.isShort && (info.isNarrow ? "fc-monarch-8ub" : "fc-monarch-x96")),
|
||||
columnEventTitleClass: (info) => joinClassNames("fc-monarch-1Zl", !info.isShort && (info.isNarrow ? "fc-monarch-2rx" : "fc-monarch-Jhn")),
|
||||
/* More-Link
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
moreLinkClass: `${outlineWidthFocusClass} ${outlineColorClass}`,
|
||||
moreLinkInnerClass: "fc-monarch-TZ4 fc-monarch-pKG",
|
||||
columnMoreLinkClass: `fc-monarch-Ika fc-monarch-wsy fc-monarch-d0j fc-monarch-4MR fc-monarch-Fvv ${strongSolidPressableClass} fc-monarch-vwH fc-monarch-A3h fc-monarch-SC5`,
|
||||
columnMoreLinkInnerClass: (info) => (info.isNarrow
|
||||
? `fc-monarch-KUX ${xxsTextClass}`
|
||||
: "fc-monarch-iS4 fc-monarch-a3B"),
|
||||
/* Day Header
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
dayHeaderAlign: "center",
|
||||
dayHeaderClass: (info) => joinClassNames("fc-monarch-E9P", info.isMajor && "fc-monarch-wsy fc-monarch-zOd", (info.isDisabled && !info.inPopover) && "fc-monarch-VTw"),
|
||||
dayHeaderInnerClass: "fc-monarch-bCs fc-monarch-9Rk fc-monarch-fn8 fc-monarch-dl1 fc-monarch-sgX fc-monarch-XpK fc-monarch-hS8",
|
||||
dayHeaderContent: (info) => (u(S, { children: [info.weekdayText && (u("div", { className: "fc-monarch-a3B fc-monarch-XHd fc-monarch-JMv", children: info.weekdayText })), info.dayNumberText && (u("div", { className: joinClassNames("fc-monarch-XE0 fc-monarch-AAA fc-monarch-dl1 fc-monarch-1sP fc-monarch-XpK fc-monarch-E9P", info.isNarrow
|
||||
? "fc-monarch-IY5 fc-monarch-1Po"
|
||||
: "fc-monarch-n6w fc-monarch-9ZS", info.isToday
|
||||
? (info.hasNavLink ? tertiaryPressableGroupClass : tertiaryClass)
|
||||
: (info.hasNavLink && mutedHoverPressableGroupClass), info.hasNavLink && `${outlineWidthGroupFocusClass} ${outlineColorClass}`), children: info.dayNumberText }))] })),
|
||||
/* Day Cell
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
dayCellClass: (info) => joinClassNames("fc-monarch-wsy", info.isMajor ? "fc-monarch-zOd" : "fc-monarch-jdl", info.isDisabled && "fc-monarch-VTw"),
|
||||
dayCellTopClass: (info) => joinClassNames("fc-monarch-dl1 fc-monarch-1sP", info.isNarrow
|
||||
? "fc-monarch-LMv fc-monarch-toR"
|
||||
: "fc-monarch-E9P fc-monarch-84e"),
|
||||
dayCellTopInnerClass: (info) => joinClassNames("fc-monarch-dl1 fc-monarch-1sP fc-monarch-XpK fc-monarch-E9P fc-monarch-TZ4 fc-monarch-AAA", info.isNarrow
|
||||
? `fc-monarch-TT0 fc-monarch-oM6 ${xxsTextClass}`
|
||||
: "fc-monarch-cGD fc-monarch-TFV fc-monarch-9yp", info.text === info.dayNumberText
|
||||
? (info.isNarrow ? "fc-monarch-79F" : "fc-monarch-ilz")
|
||||
: (info.isNarrow ? "fc-monarch-aCI" : "fc-monarch-Nca"), info.isToday
|
||||
? (info.hasNavLink ? tertiaryPressableClass : tertiaryClass)
|
||||
: (info.hasNavLink && mutedHoverPressableClass), info.isOther && "fc-monarch-bZ0", info.monthText && "fc-monarch-DIS"),
|
||||
dayCellInnerClass: (info) => joinClassNames(info.inPopover && "fc-monarch-3N5"),
|
||||
/* Popover
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
popoverFormat: { day: "numeric", weekday: "short" },
|
||||
popoverClass: "fc-monarch-wsy fc-monarch-jdl fc-monarch-hny fc-monarch-pKG fc-monarch-bvX fc-monarch-9Mx fc-monarch-sT2 fc-monarch-1kP fc-monarch-xcS fc-monarch-n5m",
|
||||
popoverCloseClass: `fc-monarch-bCs fc-monarch-1EY fc-monarch-SKv fc-monarch-aYN fc-monarch-n6w fc-monarch-AAA fc-monarch-XpK fc-monarch-E9P ${mutedHoverPressableClass} ${outlineWidthFocusClass} ${outlineColorClass} fc-monarch-Z9U`,
|
||||
popoverCloseContent: () => x(`fc-monarch-XUJ ${mutedFgPressableGroupClass}`),
|
||||
/* Lane
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
dayLaneClass: (info) => joinClassNames("fc-monarch-wsy", info.isMajor ? "fc-monarch-zOd" : "fc-monarch-jdl", info.isDisabled && "fc-monarch-VTw"),
|
||||
dayLaneInnerClass: (info) => (info.isStack
|
||||
? "fc-monarch-gMS"
|
||||
: info.isNarrow ? "fc-monarch-148" : "fc-monarch-Jzj fc-monarch-B3G"),
|
||||
slotLaneClass: (info) => joinClassNames("fc-monarch-wsy fc-monarch-jdl", info.isMinor && "fc-monarch-TN2"),
|
||||
/* List Day
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
listDayFormat: { day: "numeric" },
|
||||
listDayAltFormat: { month: "short", weekday: "short", forceCommas: true },
|
||||
listDayClass: (info) => joinClassNames(!info.isLast && "fc-monarch-zi1 fc-monarch-jdl", "fc-monarch-dl1 fc-monarch-1sP fc-monarch-EF4"),
|
||||
listDayHeaderClass: "fc-monarch-3N5 fc-monarch-yi0 fc-monarch-CjM fc-monarch-0i7 fc-monarch-l5b fc-monarch-dl1 fc-monarch-1sP fc-monarch-XpK fc-monarch-tgZ",
|
||||
listDayHeaderInnerClass: (info) => (!info.level
|
||||
? joinClassNames("fc-monarch-46Q fc-monarch-AAA fc-monarch-dl1 fc-monarch-1sP fc-monarch-XpK fc-monarch-9ZS", info.text === info.dayNumberText
|
||||
? "fc-monarch-Dhe fc-monarch-E9P"
|
||||
: "fc-monarch-Apf", info.isToday
|
||||
? (info.hasNavLink ? tertiaryPressableClass : tertiaryClass)
|
||||
: (info.hasNavLink && mutedHoverPressableClass))
|
||||
: joinClassNames("fc-monarch-a3B fc-monarch-XHd", info.hasNavLink && "fc-monarch-Eu0")),
|
||||
listDayBodyClass: "fc-monarch-1El fc-monarch-2KU fc-monarch-dl6 fc-monarch-NWN",
|
||||
/* Single Month (in Multi-Month)
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
singleMonthClass: (info) => joinClassNames(info.multiMonthColumns > 1 && "fc-monarch-jD5", (info.multiMonthColumns === 1 && !info.isLast) &&
|
||||
"fc-monarch-jdl fc-monarch-zi1"),
|
||||
singleMonthHeaderClass: (info) => joinClassNames(info.multiMonthColumns > 1
|
||||
? "fc-monarch-lTO"
|
||||
: "fc-monarch-Jhn fc-monarch-zi1 fc-monarch-jdl fc-monarch-MAH", "fc-monarch-XpK"),
|
||||
singleMonthHeaderInnerClass: (info) => joinClassNames("fc-monarch-Apf fc-monarch-Jhn fc-monarch-AAA fc-monarch-1Po fc-monarch-DIS", info.hasNavLink && mutedHoverPressableClass),
|
||||
/* Misc Table
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
tableHeaderClass: "fc-monarch-MAH",
|
||||
fillerClass: (info) => joinClassNames("fc-monarch-lMo fc-monarch-wsy", info.inTableHeader ? "fc-monarch-d0j" : "fc-monarch-jdl"),
|
||||
dayNarrowWidth: 100,
|
||||
dayHeaderRowClass: "fc-monarch-wsy fc-monarch-jdl",
|
||||
dayRowClass: "fc-monarch-wsy fc-monarch-jdl",
|
||||
/* Misc Content
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
navLinkClass: `${outlineWidthFocusClass} ${outlineColorClass}`,
|
||||
inlineWeekNumberClass: (info) => joinClassNames("fc-monarch-1EY fc-monarch-dl1 fc-monarch-1sP fc-monarch-XpK fc-monarch-TZ4", info.isNarrow
|
||||
? `fc-monarch-2ik fc-monarch-rbS fc-monarch-SEP fc-monarch-9ml fc-monarch-Utm fc-monarch-dOc ${xxsTextClass}`
|
||||
: "fc-monarch-1b8 fc-monarch-tHH fc-monarch-TFV fc-monarch-Nca fc-monarch-AAA fc-monarch-9yp", info.hasNavLink
|
||||
? secondaryPressableClass
|
||||
: secondaryClass),
|
||||
nonBusinessHoursClass: "fc-monarch-VTw",
|
||||
highlightClass: "fc-monarch-PHz",
|
||||
nowIndicatorLineClass: "fc-monarch-CH7 fc-monarch-qQW fc-monarch-nKh",
|
||||
nowIndicatorDotClass: "fc-monarch-aAW fc-monarch-Vpk fc-monarch-nKh fc-monarch-63n fc-monarch-AAA fc-monarch-GBJ fc-monarch-SC5",
|
||||
/* Resource Day Header
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
resourceDayHeaderAlign: "center",
|
||||
resourceDayHeaderClass: (info) => joinClassNames("fc-monarch-wsy", info.isMajor ? "fc-monarch-zOd" : "fc-monarch-jdl"),
|
||||
resourceDayHeaderInnerClass: (info) => joinClassNames("fc-monarch-bvX fc-monarch-dl1 fc-monarch-sgX", info.isNarrow ? "fc-monarch-a3B" : "fc-monarch-9yp"),
|
||||
/* Resource Data Grid
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
resourceColumnHeaderClass: "fc-monarch-wsy fc-monarch-jdl fc-monarch-E9P",
|
||||
resourceColumnHeaderInnerClass: "fc-monarch-bvX fc-monarch-9yp",
|
||||
resourceColumnResizerClass: "fc-monarch-1EY fc-monarch-AWB fc-monarch-4Tv fc-monarch-dnf",
|
||||
resourceGroupHeaderClass: "fc-monarch-wsy fc-monarch-jdl fc-monarch-VTw",
|
||||
resourceGroupHeaderInnerClass: "fc-monarch-bvX fc-monarch-9yp",
|
||||
resourceCellClass: "fc-monarch-wsy fc-monarch-jdl",
|
||||
resourceCellInnerClass: "fc-monarch-bvX fc-monarch-9yp",
|
||||
resourceIndentClass: "fc-monarch-Wga fc-monarch-p9t fc-monarch-E9P",
|
||||
resourceExpanderClass: `fc-monarch-bCs fc-monarch-iS4 fc-monarch-AAA ${mutedHoverPressableClass} ${outlineWidthFocusClass} ${outlineColorClass}`,
|
||||
resourceExpanderContent: (info) => chevronDown(joinClassNames(`fc-monarch-vnf ${mutedFgPressableGroupClass}`, !info.isExpanded && "fc-monarch-KxI fc-monarch-ZW3")),
|
||||
resourceHeaderRowClass: "fc-monarch-wsy fc-monarch-jdl",
|
||||
resourceRowClass: "fc-monarch-wsy fc-monarch-jdl",
|
||||
resourceColumnDividerClass: "fc-monarch-USt fc-monarch-zOd",
|
||||
/* Timeline Lane
|
||||
--------------------------------------------------------------------------------------------- */
|
||||
resourceGroupLaneClass: "fc-monarch-wsy fc-monarch-jdl fc-monarch-VTw",
|
||||
resourceLaneClass: "fc-monarch-wsy fc-monarch-jdl",
|
||||
resourceLaneBottomClass: (info) => info.options.eventOverlap && "fc-monarch-uuA",
|
||||
timelineBottomClass: "fc-monarch-uuA",
|
||||
},
|
||||
views: {
|
||||
dayGrid: {
|
||||
...dayRowCommonClasses,
|
||||
dayCellBottomClass: getShortDayCellBottomClass,
|
||||
},
|
||||
multiMonth: {
|
||||
...dayRowCommonClasses,
|
||||
dayCellBottomClass: getShortDayCellBottomClass,
|
||||
dayHeaderInnerClass: (info) => !info.inPopover && "fc-monarch-ohi",
|
||||
dayHeaderDividerClass: (info) => joinClassNames(info.multiMonthColumns === 1 &&
|
||||
"fc-monarch-zi1 fc-monarch-jdl"),
|
||||
tableBodyClass: (info) => joinClassNames(info.multiMonthColumns > 1 &&
|
||||
"fc-monarch-wsy fc-monarch-jdl fc-monarch-Fvv fc-monarch-pKG"),
|
||||
},
|
||||
timeGrid: {
|
||||
...dayRowCommonClasses,
|
||||
dayCellBottomClass: tallDayCellBottomClass,
|
||||
/* TimeGrid > Week Number Header
|
||||
------------------------------------------------------------------------------------------- */
|
||||
weekNumberHeaderClass: "fc-monarch-XpK fc-monarch-LMv",
|
||||
weekNumberHeaderInnerClass: (info) => joinClassNames("fc-monarch-Wga fc-monarch-2tF fc-monarch-dl1 fc-monarch-1sP fc-monarch-XpK fc-monarch-AAA", info.options.dayMinWidth !== undefined && "fc-monarch-KYn", info.isNarrow
|
||||
? "fc-monarch-oM6 fc-monarch-ZrE fc-monarch-a3B"
|
||||
: "fc-monarch-TFV fc-monarch-Nca fc-monarch-9yp", info.hasNavLink
|
||||
? secondaryPressableClass
|
||||
: secondaryClass),
|
||||
/* TimeGrid > All-Day Header
|
||||
------------------------------------------------------------------------------------------- */
|
||||
allDayHeaderClass: "fc-monarch-XpK fc-monarch-LMv",
|
||||
allDayHeaderInnerClass: (info) => joinClassNames("fc-monarch-bvX fc-monarch-2HE", info.isNarrow ? xxsTextClass : "fc-monarch-9yp"),
|
||||
allDayDividerClass: "fc-monarch-zi1 fc-monarch-jdl",
|
||||
/* TimeGrid > Slot Header
|
||||
------------------------------------------------------------------------------------------- */
|
||||
slotHeaderClass: (info) => joinClassNames("fc-monarch-hza fc-monarch-OBr fc-monarch-LMv fc-monarch-wsy fc-monarch-jdl", info.isMinor && "fc-monarch-TN2"),
|
||||
slotHeaderInnerClass: (info) => joinClassNames("fc-monarch-eYX fc-monarch-Mde fc-monarch-OQ9 fc-monarch-2tF", info.isNarrow
|
||||
? `fc-monarch-uqG ${xxsTextClass}`
|
||||
: "fc-monarch-Fn5 fc-monarch-9yp", info.isFirst && "fc-monarch-pps"),
|
||||
slotHeaderDividerClass: (info) => joinClassNames("fc-monarch-USt", (info.inTableHeader && info.options.dayMinWidth === undefined)
|
||||
? "fc-monarch-d0j"
|
||||
: "fc-monarch-jdl"),
|
||||
},
|
||||
list: {
|
||||
/* List-View > List-Item Event
|
||||
------------------------------------------------------------------------------------------- */
|
||||
listItemEventClass: "fc-monarch-bCs fc-monarch-3N5 fc-monarch-NYF fc-monarch-tgZ",
|
||||
listItemEventBeforeClass: "fc-monarch-fn8 fc-monarch-yDA",
|
||||
listItemEventInnerClass: "fc-monarch-tgZ fc-monarch-9yp",
|
||||
listItemEventTimeClass: "fc-monarch-yi0 fc-monarch-roZ fc-monarch-aHX fc-monarch-TZ4 fc-monarch-pKG fc-monarch-IPx",
|
||||
listItemEventTitleClass: (info) => joinClassNames("fc-monarch-1El fc-monarch-2KU fc-monarch-TZ4 fc-monarch-pKG", info.event.url && "fc-monarch-Ogp"),
|
||||
/* No-Events Screen
|
||||
------------------------------------------------------------------------------------------- */
|
||||
noEventsClass: "fc-monarch-1El fc-monarch-dl1 fc-monarch-sgX fc-monarch-XpK fc-monarch-E9P",
|
||||
noEventsInnerClass: "fc-monarch-P9h",
|
||||
},
|
||||
resourceTimeGrid: resourceDayHeaderClasses,
|
||||
resourceDayGrid: resourceDayHeaderClasses,
|
||||
timeline: {
|
||||
/* Timeline > Row Event
|
||||
------------------------------------------------------------------------------------------- */
|
||||
rowEventClass: (info) => joinClassNames(info.isEnd && "fc-monarch-9hC"),
|
||||
rowEventInnerClass: (info) => info.options.eventOverlap ? "fc-monarch-Jhn" : "fc-monarch-dl6",
|
||||
/* Timeline > More-Link
|
||||
------------------------------------------------------------------------------------------- */
|
||||
rowMoreLinkClass: `fc-monarch-9hC fc-monarch-Ika fc-monarch-Fvv fc-monarch-wsy fc-monarch-d0j fc-monarch-4MR ${strongSolidPressableClass} fc-monarch-vwH`,
|
||||
rowMoreLinkInnerClass: "fc-monarch-iS4 fc-monarch-a3B",
|
||||
/* Timeline > Slot Header
|
||||
------------------------------------------------------------------------------------------- */
|
||||
slotHeaderSticky: "0.5rem",
|
||||
slotHeaderAlign: (info) => ((info.level || info.isTime)
|
||||
? "start"
|
||||
: "center"),
|
||||
slotHeaderClass: (info) => joinClassNames("fc-monarch-wsy", info.level
|
||||
? "fc-monarch-d0j fc-monarch-Bsl"
|
||||
: joinClassNames("fc-monarch-jdl", info.isTime
|
||||
? "fc-monarch-uuA fc-monarch-OBr fc-monarch-LMv"
|
||||
: "fc-monarch-E9P")),
|
||||
slotHeaderInnerClass: (info) => joinClassNames("fc-monarch-9yp", info.level
|
||||
? joinClassNames("fc-monarch-cJ3 fc-monarch-Nca fc-monarch-Jhn fc-monarch-AAA", info.hasNavLink
|
||||
? secondaryPressableClass
|
||||
: secondaryClass)
|
||||
: joinClassNames("fc-monarch-Nca", info.isTime
|
||||
? joinClassNames("fc-monarch-b0n fc-monarch-eYX fc-monarch-4oC", info.isFirst && "fc-monarch-pps")
|
||||
: "fc-monarch-dl6", info.hasNavLink && "fc-monarch-Eu0")),
|
||||
slotHeaderDividerClass: "fc-monarch-zi1 fc-monarch-jdl",
|
||||
},
|
||||
}
|
||||
};
|
||||
/* SVGs
|
||||
------------------------------------------------------------------------------------------------- */
|
||||
function chevronDown(className) {
|
||||
return u("svg", { xmlns: "http://www.w3.org/2000/svg", className: className, width: "20", height: "20", viewBox: "80 -880 800 800", fill: "currentColor", children: u("path", { d: "M480-304 240-544l56-56 184 184 184-184 56 56-240 240Z" }) });
|
||||
}
|
||||
function chevronDoubleLeft(className) {
|
||||
return u("svg", { xmlns: "http://www.w3.org/2000/svg", className: className, width: "20", height: "20", viewBox: "80 -880 800 800", fill: "currentColor", children: u("path", { d: "M440-240 200-480l240-240 56 56-183 184 183 184-56 56Zm264 0L464-480l240-240 56 56-183 184 183 184-56 56Z" }) });
|
||||
}
|
||||
function x(className) {
|
||||
return u("svg", { xmlns: "http://www.w3.org/2000/svg", className: className, width: "20", height: "20", viewBox: "80 -880 800 800", fill: "currentColor", children: u("path", { d: "m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" }) });
|
||||
}
|
||||
function filledRightTriangle(className) {
|
||||
return (u("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 800 2200", preserveAspectRatio: "none", className: className, children: u("polygon", { points: "0,0 66,0 800,1100 66,2200 0,2200", fill: "currentColor" }) }));
|
||||
}
|
||||
|
||||
globalPlugins.push(index);
|
||||
|
||||
})(FullCalendar.Shared);
|
||||
104
pub/src/css/fullcalendar/theme/palettes/blue.css
Normal file
104
pub/src/css/fullcalendar/theme/palettes/blue.css
Normal file
@@ -0,0 +1,104 @@
|
||||
|
||||
:root {
|
||||
/* primary */
|
||||
--fc-monarch-primary: rgb(65 95 145);
|
||||
--fc-monarch-primary-foreground: rgb(255 255 255);
|
||||
--fc-monarch-primary-over: #526e9c;
|
||||
--fc-monarch-primary-down: #647ea7;
|
||||
|
||||
/* secondary */
|
||||
--fc-monarch-secondary: rgb(214 227 255);
|
||||
--fc-monarch-secondary-foreground: rgb(40 71 119);
|
||||
--fc-monarch-secondary-over: #c9d6f2;
|
||||
--fc-monarch-secondary-down: #bdc9e5;
|
||||
|
||||
/* tertiary */
|
||||
--fc-monarch-tertiary: rgb(133 93 140);
|
||||
--fc-monarch-tertiary-foreground: rgb(255 255 255);
|
||||
--fc-monarch-tertiary-over: #916c97;
|
||||
--fc-monarch-tertiary-down: #9d7ca2;
|
||||
|
||||
/* calendar content */
|
||||
--fc-monarch-event: var(--fc-monarch-primary);
|
||||
--fc-monarch-event-contrast: var(--fc-monarch-primary-foreground);
|
||||
--fc-monarch-highlight: #d6e3ff4D;
|
||||
--fc-monarch-now: rgb(186 26 26);
|
||||
|
||||
/* controls */
|
||||
--fc-monarch-selected: rgb(86 95 113);
|
||||
--fc-monarch-selected-foreground: rgb(255 255 255);
|
||||
--fc-monarch-selected-over: #656e7e;
|
||||
--fc-monarch-selected-down: #757d8c;
|
||||
--fc-monarch-outline: #6687ff;
|
||||
|
||||
/* popover */
|
||||
--fc-monarch-popover: var(--fc-monarch-background);
|
||||
|
||||
/* neutral backgrounds */
|
||||
--fc-monarch-background: #ffffff;
|
||||
--fc-monarch-faint: #64748b1A;
|
||||
--fc-monarch-muted: #64748b26;
|
||||
--fc-monarch-strong: #64748b4D;
|
||||
--fc-monarch-stronger: #64748b59;
|
||||
--fc-monarch-strongest: #64748b66;
|
||||
|
||||
/* neutral foregrounds */
|
||||
--fc-monarch-foreground: #020617;
|
||||
--fc-monarch-faint-foreground: #bebebe;
|
||||
--fc-monarch-muted-foreground: #8d8d8d;
|
||||
|
||||
/* neutral borders */
|
||||
--fc-monarch-border: #dddee1;
|
||||
--fc-monarch-strong-border: #c2c3c7;
|
||||
}
|
||||
|
||||
@media not print {
|
||||
[data-color-scheme=dark] {
|
||||
/* primary */
|
||||
--fc-monarch-primary: rgb(170 199 255);
|
||||
--fc-monarch-primary-foreground: rgb(10 48 95);
|
||||
--fc-monarch-primary-over: #b2cdff;
|
||||
--fc-monarch-primary-down: #bbd2ff;
|
||||
|
||||
/* secondary */
|
||||
--fc-monarch-secondary: rgb(40 71 119);
|
||||
--fc-monarch-secondary-foreground: rgb(214 227 255);
|
||||
--fc-monarch-secondary-over: #32507e;
|
||||
--fc-monarch-secondary-down: #3c5885;
|
||||
|
||||
/* tertiary */
|
||||
--fc-monarch-tertiary: rgb(221 188 224);
|
||||
--fc-monarch-tertiary-foreground: rgb(63 40 68);
|
||||
--fc-monarch-tertiary-over: #e0c3e3;
|
||||
--fc-monarch-tertiary-down: #e4c9e6;
|
||||
|
||||
/* calendar content */
|
||||
--fc-monarch-event: var(--fc-monarch-primary);
|
||||
--fc-monarch-event-contrast: var(--fc-monarch-primary-foreground);
|
||||
--fc-monarch-highlight: #2847774D;
|
||||
--fc-monarch-now: rgb(255 180 171);
|
||||
|
||||
/* controls */
|
||||
--fc-monarch-selected: rgb(190 198 220);
|
||||
--fc-monarch-selected-foreground: rgb(40 49 65);
|
||||
--fc-monarch-selected-over: #c4cce0;
|
||||
--fc-monarch-selected-down: #cbd1e3;
|
||||
|
||||
/* popover */
|
||||
--fc-monarch-popover: rgb(17 19 24);
|
||||
--fc-monarch-popover-foreground: rgb(226 226 233);
|
||||
|
||||
/* neutral backgrounds */
|
||||
--fc-monarch-background: #030712;
|
||||
|
||||
/* neutral foregrounds */
|
||||
--fc-monarch-foreground: #ffffff;
|
||||
--fc-monarch-faint-foreground: #898989;
|
||||
--fc-monarch-muted-foreground: #b0b0b0;
|
||||
|
||||
/* neutral borders */
|
||||
--fc-monarch-border: #313234;
|
||||
--fc-monarch-strong-border: #53555a;
|
||||
}
|
||||
}
|
||||
|
||||
105
pub/src/css/fullcalendar/theme/palettes/green.css
Normal file
105
pub/src/css/fullcalendar/theme/palettes/green.css
Normal file
@@ -0,0 +1,105 @@
|
||||
|
||||
:root {
|
||||
/* primary */
|
||||
--fc-monarch-primary: rgb(76 102 43);
|
||||
--fc-monarch-primary-foreground: rgb(255 255 255);
|
||||
--fc-monarch-primary-over: #5c7440;
|
||||
--fc-monarch-primary-down: #6d8354;
|
||||
|
||||
/* secondary */
|
||||
--fc-monarch-secondary: rgb(205 237 163);
|
||||
--fc-monarch-secondary-foreground: rgb(53 78 22);
|
||||
--fc-monarch-secondary-over: #c2e09a;
|
||||
--fc-monarch-secondary-down: #b7d391;
|
||||
|
||||
/* tertiary */
|
||||
--fc-monarch-tertiary: rgb(56 102 99);
|
||||
--fc-monarch-tertiary-foreground: rgb(255 255 255);
|
||||
--fc-monarch-tertiary-over: #4b7471;
|
||||
--fc-monarch-tertiary-down: #5f8380;
|
||||
|
||||
/* calendar content */
|
||||
--fc-monarch-event: var(--fc-monarch-primary);
|
||||
--fc-monarch-event-contrast: var(--fc-monarch-primary-foreground);
|
||||
--fc-monarch-highlight: #cdeda34D;
|
||||
--fc-monarch-now: rgb(186 26 26);
|
||||
|
||||
/* controls */
|
||||
--fc-monarch-selected: rgb(88 98 73);
|
||||
--fc-monarch-selected-foreground: rgb(255 255 255);
|
||||
--fc-monarch-selected-over: #67705a;
|
||||
--fc-monarch-selected-down: #777f6a;
|
||||
--fc-monarch-outline: #9fdb50;
|
||||
|
||||
/* popover */
|
||||
--fc-monarch-popover: var(--fc-monarch-background);
|
||||
|
||||
/* neutral backgrounds */
|
||||
--fc-monarch-background: #ffffff;
|
||||
--fc-monarch-faint: #7c7c671A;
|
||||
--fc-monarch-muted: #7c7c6726;
|
||||
--fc-monarch-strong: #7c7c674D;
|
||||
--fc-monarch-stronger: #7c7c6759;
|
||||
--fc-monarch-strongest: #7c7c6766;
|
||||
|
||||
/* neutral foregrounds */
|
||||
--fc-monarch-foreground: #0c0c09;
|
||||
--fc-monarch-faint-foreground: #bebebe;
|
||||
--fc-monarch-muted-foreground: #8d8d8d;
|
||||
|
||||
/* neutral borders */
|
||||
--fc-monarch-border: #dddfdc;
|
||||
--fc-monarch-strong-border: #c3c5c1;
|
||||
}
|
||||
|
||||
@media not print {
|
||||
[data-color-scheme=dark] {
|
||||
/* primary */
|
||||
--fc-monarch-primary: rgb(177 209 138);
|
||||
--fc-monarch-primary-foreground: rgb(31 55 1);
|
||||
--fc-monarch-primary-over: #b9d696;
|
||||
--fc-monarch-primary-down: #c0daa2;
|
||||
|
||||
/* secondary */
|
||||
--fc-monarch-secondary: rgb(53 78 22);
|
||||
--fc-monarch-secondary-foreground: rgb(205 237 163);
|
||||
--fc-monarch-secondary-over: #3e5622;
|
||||
--fc-monarch-secondary-down: #475e2e;
|
||||
|
||||
/* tertiary */
|
||||
--fc-monarch-tertiary: rgb(160 208 203);
|
||||
--fc-monarch-tertiary-foreground: rgb(0 55 53);
|
||||
--fc-monarch-tertiary-over: #aad5d0;
|
||||
--fc-monarch-tertiary-down: #b3d9d5;
|
||||
|
||||
/* calendar content */
|
||||
--fc-monarch-event: var(--fc-monarch-primary);
|
||||
--fc-monarch-event-contrast: var(--fc-monarch-primary-foreground);
|
||||
--fc-monarch-highlight: #354e164D;
|
||||
--fc-monarch-now: rgb(255 180 171);
|
||||
|
||||
/* controls */
|
||||
--fc-monarch-selected: rgb(191 203 173);
|
||||
--fc-monarch-selected-foreground: rgb(42 51 30);
|
||||
--fc-monarch-selected-over: #c5d0b5;
|
||||
--fc-monarch-selected-down: #ccd5bd;
|
||||
--fc-monarch-outline: #5bcc3a;
|
||||
|
||||
/* popover */
|
||||
--fc-monarch-popover: rgb(18 20 14);
|
||||
--fc-monarch-popover-foreground: rgb(226 227 216);
|
||||
|
||||
/* neutral backgrounds */
|
||||
--fc-monarch-background: #030712;
|
||||
|
||||
/* neutral foregrounds */
|
||||
--fc-monarch-foreground: #ffffff;
|
||||
--fc-monarch-faint-foreground: #898989;
|
||||
--fc-monarch-muted-foreground: #b0b0b0;
|
||||
|
||||
/* neutral borders */
|
||||
--fc-monarch-border: #313330;
|
||||
--fc-monarch-strong-border: #525650;
|
||||
}
|
||||
}
|
||||
|
||||
55
pub/src/css/fullcalendar/theme/palettes/purple.css
Normal file
55
pub/src/css/fullcalendar/theme/palettes/purple.css
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
:root {
|
||||
/* primary */
|
||||
--fc-monarch-primary: var(--bs-primary);
|
||||
--fc-monarch-primary-foreground: var(--bs-body-color);
|
||||
--fc-monarch-primary-over: var(--bs-primary-text-emphasis);
|
||||
--fc-monarch-primary-down: #8272b7;
|
||||
|
||||
/* secondary */
|
||||
--fc-monarch-secondary: var(--bs-secondary-bg);
|
||||
--fc-monarch-secondary-foreground: var(--bs-body-color);
|
||||
--fc-monarch-secondary-over: var(--bs-secondary-text-emphasis);
|
||||
--fc-monarch-secondary-down: #d0c4e3;
|
||||
|
||||
/* tertiary */
|
||||
--fc-monarch-tertiary: var(--bs-success);
|
||||
--fc-monarch-tertiary-foreground: var(--bs-body-color);
|
||||
--fc-monarch-tertiary-over: #956272;
|
||||
--fc-monarch-tertiary-down: #a17381;
|
||||
|
||||
/* calendar content */
|
||||
--fc-monarch-event: var(--fc-monarch-primary);
|
||||
--fc-monarch-event-contrast: var(--fc-monarch-primary-foreground);
|
||||
--fc-monarch-highlight: var(--bs-hover-bg);
|
||||
--fc-monarch-now: #B3261E;
|
||||
|
||||
/* controls */
|
||||
--fc-monarch-selected: #625B71;
|
||||
--fc-monarch-selected-foreground: #FFFFFF;
|
||||
--fc-monarch-selected-over: #706a7e;
|
||||
--fc-monarch-selected-down: #7f798c;
|
||||
--fc-monarch-outline: #ff9dbb;
|
||||
|
||||
/* popover */
|
||||
--fc-monarch-popover: var(--fc-monarch-background);
|
||||
|
||||
/* neutral backgrounds */
|
||||
--fc-monarch-background: var(--bs-body-bg);
|
||||
--fc-monarch-faint: var(--bs-tertiary-bg);
|
||||
--fc-monarch-muted: var(--bs-tertiary-color);
|
||||
--fc-monarch-strong: #79697b4D;
|
||||
--fc-monarch-stronger: #79697b59;
|
||||
--fc-monarch-strongest: #79697b66;
|
||||
|
||||
/* neutral foregrounds */
|
||||
--fc-monarch-foreground: var(--bs-body-color);
|
||||
--fc-monarch-faint-foreground: #bebebe;
|
||||
--fc-monarch-muted-foreground: var(--bs-tertiary-color);
|
||||
|
||||
/* neutral borders */
|
||||
--fc-monarch-border: var(--bs-border-color);
|
||||
--fc-monarch-strong-border: var(--bs-border-color);
|
||||
}
|
||||
|
||||
|
||||
105
pub/src/css/fullcalendar/theme/palettes/red.css
Normal file
105
pub/src/css/fullcalendar/theme/palettes/red.css
Normal file
@@ -0,0 +1,105 @@
|
||||
|
||||
:root {
|
||||
/* primary */
|
||||
--fc-monarch-primary: rgb(143 76 56);
|
||||
--fc-monarch-primary-foreground: rgb(255 255 255);
|
||||
--fc-monarch-primary-over: #9b5d4b;
|
||||
--fc-monarch-primary-down: #a76e5e;
|
||||
|
||||
/* secondary */
|
||||
--fc-monarch-secondary: rgb(255 219 209);
|
||||
--fc-monarch-secondary-foreground: rgb(114 53 35);
|
||||
--fc-monarch-secondary-over: #f1cfc5;
|
||||
--fc-monarch-secondary-down: #e3c3ba;
|
||||
|
||||
/* tertiary */
|
||||
--fc-monarch-tertiary: rgb(108 93 47);
|
||||
--fc-monarch-tertiary-foreground: rgb(255 255 255);
|
||||
--fc-monarch-tertiary-over: #7a6c43;
|
||||
--fc-monarch-tertiary-down: #887b57;
|
||||
|
||||
/* calendar content */
|
||||
--fc-monarch-event: var(--fc-monarch-primary);
|
||||
--fc-monarch-event-contrast: var(--fc-monarch-primary-foreground);
|
||||
--fc-monarch-highlight: #ffdbd14D;
|
||||
--fc-monarch-now: rgb(186 26 26);
|
||||
|
||||
/* controls */
|
||||
--fc-monarch-selected: rgb(119 87 78);
|
||||
--fc-monarch-selected-foreground: rgb(255 255 255);
|
||||
--fc-monarch-selected-over: #84665e;
|
||||
--fc-monarch-selected-down: #91766f;
|
||||
--fc-monarch-outline: #ff774f;
|
||||
|
||||
/* popover */
|
||||
--fc-monarch-popover: var(--fc-monarch-background);
|
||||
|
||||
/* neutral backgrounds */
|
||||
--fc-monarch-background: #ffffff;
|
||||
--fc-monarch-faint: #7c6d671A;
|
||||
--fc-monarch-muted: #7c6d6726;
|
||||
--fc-monarch-strong: #7c6d674D;
|
||||
--fc-monarch-stronger: #7c6d6759;
|
||||
--fc-monarch-strongest: #7c6d6766;
|
||||
|
||||
/* neutral foregrounds */
|
||||
--fc-monarch-foreground: #0c0a09;
|
||||
--fc-monarch-faint-foreground: #bebebe;
|
||||
--fc-monarch-muted-foreground: #8d8d8d;
|
||||
|
||||
/* neutral borders */
|
||||
--fc-monarch-border: #e2dedd;
|
||||
--fc-monarch-strong-border: #c9c4c3;
|
||||
}
|
||||
|
||||
@media not print {
|
||||
[data-color-scheme=dark] {
|
||||
/* primary */
|
||||
--fc-monarch-primary: rgb(255 181 160);
|
||||
--fc-monarch-primary-foreground: rgb(86 31 15);
|
||||
--fc-monarch-primary-over: #ffbda9;
|
||||
--fc-monarch-primary-down: #ffc4b3;
|
||||
|
||||
/* secondary */
|
||||
--fc-monarch-secondary: rgb(114 53 35);
|
||||
--fc-monarch-secondary-foreground: rgb(255 219 209);
|
||||
--fc-monarch-secondary-over: #793f2d;
|
||||
--fc-monarch-secondary-down: #814837;
|
||||
|
||||
/* tertiary */
|
||||
--fc-monarch-tertiary: rgb(216 197 141);
|
||||
--fc-monarch-tertiary-foreground: rgb(59 47 5);
|
||||
--fc-monarch-tertiary-over: #dccb99;
|
||||
--fc-monarch-tertiary-down: #e0d1a4;
|
||||
|
||||
/* calendar content */
|
||||
--fc-monarch-event: var(--fc-monarch-primary);
|
||||
--fc-monarch-event-contrast: var(--fc-monarch-primary-foreground);
|
||||
--fc-monarch-highlight: #7235234D;
|
||||
--fc-monarch-now: rgb(255 180 171);
|
||||
|
||||
/* controls */
|
||||
--fc-monarch-selected: rgb(231 189 178);
|
||||
--fc-monarch-selected-foreground: rgb(68 42 34);
|
||||
--fc-monarch-selected-over: #eac4ba;
|
||||
--fc-monarch-selected-down: #eccac1;
|
||||
--fc-monarch-outline: #f94e1b;
|
||||
|
||||
/* popover */
|
||||
--fc-monarch-popover: rgb(26 17 15);
|
||||
--fc-monarch-popover-foreground: rgb(241 223 218);
|
||||
|
||||
/* neutral backgrounds */
|
||||
--fc-monarch-background: #030712;
|
||||
|
||||
/* neutral foregrounds */
|
||||
--fc-monarch-foreground: #ffffff;
|
||||
--fc-monarch-faint-foreground: #898989;
|
||||
--fc-monarch-muted-foreground: #b0b0b0;
|
||||
|
||||
/* neutral borders */
|
||||
--fc-monarch-border: #34312f;
|
||||
--fc-monarch-strong-border: #544f4d;
|
||||
}
|
||||
}
|
||||
|
||||
105
pub/src/css/fullcalendar/theme/palettes/yellow.css
Normal file
105
pub/src/css/fullcalendar/theme/palettes/yellow.css
Normal file
@@ -0,0 +1,105 @@
|
||||
|
||||
:root {
|
||||
/* primary */
|
||||
--fc-monarch-primary: rgb(109 94 15);
|
||||
--fc-monarch-primary-foreground: rgb(255 255 255);
|
||||
--fc-monarch-primary-over: #7a6d2e;
|
||||
--fc-monarch-primary-down: #887d47;
|
||||
|
||||
/* secondary */
|
||||
--fc-monarch-secondary: rgb(248 226 135);
|
||||
--fc-monarch-secondary-foreground: rgb(83 70 0);
|
||||
--fc-monarch-secondary-over: #ead680;
|
||||
--fc-monarch-secondary-down: #ddc979;
|
||||
|
||||
/* tertiary */
|
||||
--fc-monarch-tertiary: rgb(67 102 78);
|
||||
--fc-monarch-tertiary-foreground: rgb(255 255 255);
|
||||
--fc-monarch-tertiary-over: #55745e;
|
||||
--fc-monarch-tertiary-down: #66836f;
|
||||
|
||||
/* calendar content */
|
||||
--fc-monarch-event: var(--fc-monarch-primary);
|
||||
--fc-monarch-event-contrast: var(--fc-monarch-primary-foreground);
|
||||
--fc-monarch-highlight: #f8e2874D;
|
||||
--fc-monarch-now: rgb(186 26 26);
|
||||
|
||||
/* controls */
|
||||
--fc-monarch-selected: rgb(102 94 64);
|
||||
--fc-monarch-selected-foreground: rgb(255 255 255);
|
||||
--fc-monarch-selected-over: #746d52;
|
||||
--fc-monarch-selected-down: #827c63;
|
||||
--fc-monarch-outline: #ffbd66;
|
||||
|
||||
/* popover */
|
||||
--fc-monarch-popover: var(--fc-monarch-background);
|
||||
|
||||
/* neutral backgrounds */
|
||||
--fc-monarch-background: #ffffff;
|
||||
--fc-monarch-faint: #7c7c671A;
|
||||
--fc-monarch-muted: #7c7c6726;
|
||||
--fc-monarch-strong: #7c7c674D;
|
||||
--fc-monarch-stronger: #7c7c6759;
|
||||
--fc-monarch-strongest: #7c7c6766;
|
||||
|
||||
/* neutral foregrounds */
|
||||
--fc-monarch-foreground: #0c0c09;
|
||||
--fc-monarch-faint-foreground: #bebebe;
|
||||
--fc-monarch-muted-foreground: #8d8d8d;
|
||||
|
||||
/* neutral borders */
|
||||
--fc-monarch-border: #e0dfdb;
|
||||
--fc-monarch-strong-border: #c5c3bf;
|
||||
}
|
||||
|
||||
@media not print {
|
||||
[data-color-scheme=dark] {
|
||||
/* primary */
|
||||
--fc-monarch-primary: rgb(219 198 110);
|
||||
--fc-monarch-primary-foreground: rgb(58 48 0);
|
||||
--fc-monarch-primary-over: #dfcc7e;
|
||||
--fc-monarch-primary-down: #e2d28d;
|
||||
|
||||
/* secondary */
|
||||
--fc-monarch-secondary: rgb(83 70 0);
|
||||
--fc-monarch-secondary-foreground: rgb(248 226 135);
|
||||
--fc-monarch-secondary-over: #5b4e13;
|
||||
--fc-monarch-secondary-down: #625722;
|
||||
|
||||
/* tertiary */
|
||||
--fc-monarch-tertiary: rgb(169 208 179);
|
||||
--fc-monarch-tertiary-foreground: rgb(20 55 35);
|
||||
--fc-monarch-tertiary-over: #b2d5ba;
|
||||
--fc-monarch-tertiary-down: #bad9c2;
|
||||
|
||||
/* calendar content */
|
||||
--fc-monarch-event: var(--fc-monarch-primary);
|
||||
--fc-monarch-event-contrast: var(--fc-monarch-primary-foreground);
|
||||
--fc-monarch-highlight: #5346004D;
|
||||
--fc-monarch-now: rgb(255 180 171);
|
||||
|
||||
/* controls */
|
||||
--fc-monarch-selected: rgb(209 198 161);
|
||||
--fc-monarch-selected-foreground: rgb(54 48 22);
|
||||
--fc-monarch-selected-over: #d6ccaa;
|
||||
--fc-monarch-selected-down: #dad1b4;
|
||||
--fc-monarch-outline: #ffe6c5;
|
||||
|
||||
/* popover */
|
||||
--fc-monarch-popover: rgb(21 19 11);
|
||||
--fc-monarch-popover-foreground: rgb(232 226 212);
|
||||
|
||||
/* neutral backgrounds */
|
||||
--fc-monarch-background: #030712;
|
||||
|
||||
/* neutral foregrounds */
|
||||
--fc-monarch-foreground: #ffffff;
|
||||
--fc-monarch-faint-foreground: #898989;
|
||||
--fc-monarch-muted-foreground: #b0b0b0;
|
||||
|
||||
/* neutral borders */
|
||||
--fc-monarch-border: #333230;
|
||||
--fc-monarch-strong-border: #56544f;
|
||||
}
|
||||
}
|
||||
|
||||
1085
pub/src/css/fullcalendar/theme/theme.css
Normal file
1085
pub/src/css/fullcalendar/theme/theme.css
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3492,7 +3492,7 @@ label {
|
||||
.form-control {
|
||||
font-size: 1rem;
|
||||
border-color: var(--bs-border-color-translucent);
|
||||
padding: 0.6rem 1rem;
|
||||
padding: 0.375rem 2.25rem 0.375rem 0.75rem;
|
||||
/* height: inherit !important; disable because of resizing issues in textarea*/
|
||||
border-width: 2px;
|
||||
}
|
||||
@@ -4138,11 +4138,11 @@ label.error {
|
||||
}
|
||||
|
||||
.table > tbody > tr > td, .table > tbody > tr > th {
|
||||
padding: 12px 24px !important;
|
||||
padding: 12px 24px;
|
||||
}
|
||||
|
||||
.table > tfoot > tr > td, .table > tfoot > tr > th {
|
||||
padding: 12px 24px !important;
|
||||
padding: 12px 24px;
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
|
||||
2
pub/src/css/sentri.min.css
vendored
2
pub/src/css/sentri.min.css
vendored
File diff suppressed because one or more lines are too long
17479
pub/src/js/plugin/fullcalendar/fullcalendar.global.js
Normal file
17479
pub/src/js/plugin/fullcalendar/fullcalendar.global.js
Normal file
File diff suppressed because one or more lines are too long
34
pub/src/js/sentri/companiesMap.js
Normal file
34
pub/src/js/sentri/companiesMap.js
Normal file
@@ -0,0 +1,34 @@
|
||||
async function loadCompanies() {
|
||||
|
||||
// Use cached data if available
|
||||
const cached = localStorage.getItem('companies');
|
||||
|
||||
if (cached) {
|
||||
return JSON.parse(cached);
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
'/api/v1/customers/companies/?' +
|
||||
'builder[0][where][0]=company_state&' +
|
||||
'builder[0][where][1]=active',
|
||||
{
|
||||
credentials: 'include'
|
||||
}
|
||||
);
|
||||
|
||||
const companies = await response.json();
|
||||
|
||||
localStorage.setItem('companies', JSON.stringify(companies));
|
||||
|
||||
return companies;
|
||||
}
|
||||
|
||||
function getCompanyMap() {
|
||||
|
||||
const companies = JSON.parse(localStorage.getItem('companies') || '[]');
|
||||
|
||||
return companies.reduce((map, company) => {
|
||||
map[company.company_uuid] = company.company_name;
|
||||
return map;
|
||||
}, {});
|
||||
}
|
||||
357
pub/src/js/sentri/travelReimburse.js
Normal file
357
pub/src/js/sentri/travelReimburse.js
Normal file
@@ -0,0 +1,357 @@
|
||||
let calendar;
|
||||
let travelEventSource;
|
||||
const rootStyles = getComputedStyle(document.documentElement);
|
||||
const background = rootStyles.getPropertyValue('--swal-bg')?.trim();
|
||||
const color = rootStyles.getPropertyValue('--swal-text-color')?.trim();
|
||||
document.addEventListener('DOMContentLoaded', async function () {
|
||||
await loadCompanies();
|
||||
|
||||
const companyMap = getCompanyMap();
|
||||
|
||||
const calendarEl = document.getElementById('calendar');
|
||||
|
||||
// Build calendar
|
||||
calendar = new FullCalendar.Calendar(calendarEl, {
|
||||
initialDate: new Date().toISOString().split('T')[0],
|
||||
contentHeight: 'auto',
|
||||
initialView: 'dayGridMonth',
|
||||
aspectRatio: 3,
|
||||
fixedWeekCount: false,
|
||||
|
||||
selectable: true,
|
||||
businessHours: true,
|
||||
dayMaxEvents: true,
|
||||
showNonCurrentDates: false,
|
||||
|
||||
headerToolbar: {
|
||||
start: 'title',
|
||||
end: 'today prev,next',
|
||||
},
|
||||
|
||||
|
||||
datesSet: function (info) {
|
||||
const date = calendar.getDate();
|
||||
|
||||
document.getElementById('calender_month').value =
|
||||
`${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
|
||||
},
|
||||
|
||||
eventContent: function (arg) {
|
||||
return {html: arg.event.title};
|
||||
},
|
||||
|
||||
dateClick: function (info) {
|
||||
|
||||
const clicked = info.date;
|
||||
const current = info.view.currentStart;
|
||||
|
||||
// Compare month + year only
|
||||
const sameMonth =
|
||||
clicked.getMonth() === current.getMonth() &&
|
||||
clicked.getFullYear() === current.getFullYear();
|
||||
|
||||
if (!sameMonth) {
|
||||
return; // ignore outside-month days
|
||||
}
|
||||
|
||||
createTravelReimburse(info.dateStr);
|
||||
},
|
||||
|
||||
// Open modal on click
|
||||
eventClick: function (info) {
|
||||
|
||||
const event = info.event;
|
||||
|
||||
document.getElementById('reimburse_uuid').value = event.id;
|
||||
document.getElementById('departure_postcode').value = event.extendedProps.departure;
|
||||
document.getElementById('destination_postcode').value = event.extendedProps.destination;
|
||||
document.getElementById('travel_distance').value = event.extendedProps.distance;
|
||||
document.getElementById('travel_date').value = event.extendedProps.travel_date;
|
||||
document.getElementById('homework').checked = event.extendedProps.homework;
|
||||
document.getElementById('office_travelreimburse_company_uuid').value = event.extendedProps.company;
|
||||
document.getElementById('travel_description').value = event.extendedProps.description ?? '';
|
||||
|
||||
new bootstrap.Modal(document.getElementById('travelModal')).show();
|
||||
}
|
||||
});
|
||||
|
||||
calendar.render();
|
||||
|
||||
|
||||
// Event retrievement
|
||||
travelEventSource = calendar.addEventSource({
|
||||
events: function (fetchInfo, successCallback, failureCallback) {
|
||||
|
||||
const start = toYMD(fetchInfo.start);
|
||||
|
||||
const endDate = new Date(fetchInfo.end);
|
||||
endDate.setDate(endDate.getDate() - 1);
|
||||
|
||||
const end = toYMD(endDate);
|
||||
|
||||
fetch(`/api/v1/office/travel-reimburse/?` +
|
||||
`builder[1][between][0]=travel_date&` +
|
||||
`builder[1][between][1]=${start}&` +
|
||||
`builder[1][between][2]=${end}`, {
|
||||
credentials: 'include'
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
let totalDistanceHomework = 0;
|
||||
let totalDistanceBusiness = 0;
|
||||
|
||||
data.forEach(item => {
|
||||
const distance = parseFloat(item.travel_distance) || 0;
|
||||
|
||||
if (Number(item.homework) === 1) {
|
||||
totalDistanceHomework += distance;
|
||||
} else {
|
||||
totalDistanceBusiness += distance;
|
||||
}
|
||||
});
|
||||
|
||||
const total_homeWorkIncl = document.getElementById('total_travel_distance_homework_incl');
|
||||
const total_homeWorkExcl = document.getElementById('total_travel_distance_homework_excl');
|
||||
const total_businesskIncl = document.getElementById('total_travel_distance_business_incl');
|
||||
const total_businessExcl = document.getElementById('total_travel_distance_business_excl');
|
||||
|
||||
if (total_homeWorkExcl) {
|
||||
total_homeWorkExcl.textContent = `${totalDistanceHomework} km`;
|
||||
total_homeWorkIncl.textContent = `${totalDistanceHomework} km`;
|
||||
}
|
||||
|
||||
if (total_businesskIncl) {
|
||||
total_businesskIncl.textContent = `${totalDistanceBusiness} km`;
|
||||
total_businessExcl.textContent = `${totalDistanceBusiness} km`;
|
||||
}
|
||||
|
||||
const formatEuro = amount =>
|
||||
amount.toLocaleString('nl-NL', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
});
|
||||
|
||||
const parseEuro = (el) => {
|
||||
if (!el) return 0;
|
||||
|
||||
// "€ 0,23" -> 0.23
|
||||
return parseFloat(
|
||||
el.textContent
|
||||
.replace('€', '')
|
||||
.replace(/\s/g, '')
|
||||
.replace('.', '')
|
||||
.replace(',', '.')
|
||||
) || 0;
|
||||
};
|
||||
|
||||
const homeworkInclRate = parseEuro(document.getElementById('office_travelreimburse_cents_homework_incl'));
|
||||
const homeworkExclRate = parseEuro(document.getElementById('office_travelreimburse_cents_homework_excl'));
|
||||
const businessInclRate = parseEuro(document.getElementById('office_travelreimburse_cents_business_incl'));
|
||||
const businessExclRate = parseEuro(document.getElementById('office_travelreimburse_cents_business_excl'));
|
||||
|
||||
|
||||
document.getElementById('office_travelreimburse_cents_homework_incl_total').textContent =
|
||||
formatEuro(totalDistanceHomework * homeworkInclRate);
|
||||
|
||||
document.getElementById('office_travelreimburse_cents_homework_excl_total').textContent =
|
||||
formatEuro(totalDistanceHomework * homeworkExclRate);
|
||||
|
||||
document.getElementById('office_travelreimburse_cents_business_incl_total').textContent =
|
||||
formatEuro(totalDistanceBusiness * businessInclRate);
|
||||
|
||||
document.getElementById('office_travelreimburse_cents_business_excl_total').textContent =
|
||||
formatEuro(totalDistanceBusiness * businessExclRate);
|
||||
|
||||
document.getElementById('total_travel_incl').textContent =
|
||||
formatEuro((totalDistanceBusiness * businessInclRate) + (totalDistanceHomework * homeworkInclRate));
|
||||
|
||||
document.getElementById('total_travel_excl').textContent =
|
||||
formatEuro((totalDistanceBusiness * businessExclRate) + (totalDistanceHomework * homeworkExclRate));
|
||||
|
||||
const events = data.map(item => ({
|
||||
id: item.reimburse_uuid,
|
||||
title: `${item.travel_distance}km | ${companyMap[item.office_travelreimburse_company_uuid] ?? item.office_travelreimburse_company_uuid}`,
|
||||
start: item.travel_date,
|
||||
|
||||
...(item.homework === 1 && {
|
||||
color: 'var(--bs-success)'
|
||||
}),
|
||||
|
||||
extendedProps: {
|
||||
departure: item.departure_postcode,
|
||||
destination: item.destination_postcode,
|
||||
distance: item.travel_distance,
|
||||
travel_date: item.travel_date,
|
||||
description: item.travel_description,
|
||||
company: item.office_travelreimburse_company_uuid,
|
||||
homework: item.homework
|
||||
}
|
||||
}));
|
||||
|
||||
successCallback(events);
|
||||
})
|
||||
.catch(failureCallback);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function toYMD(dateStr) {
|
||||
const d = new Date(dateStr);
|
||||
|
||||
return d.getFullYear() + '-' +
|
||||
String(d.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(d.getDate()).padStart(2, '0');
|
||||
}
|
||||
|
||||
// Update from modal form
|
||||
document.getElementById('saveTravelBtn').addEventListener('click', function () {
|
||||
|
||||
const payload = {
|
||||
reimburse_uuid: document.getElementById('reimburse_uuid').value,
|
||||
departure_postcode: document.getElementById('departure_postcode').value,
|
||||
destination_postcode: document.getElementById('destination_postcode').value,
|
||||
travel_distance: document.getElementById('travel_distance').value,
|
||||
travel_description: document.getElementById('travel_description').value,
|
||||
travel_date: document.getElementById('travel_date').value,
|
||||
homework: document.getElementById('homework').checked,
|
||||
office_travelreimburse_company_uuid: document.getElementById('office_travelreimburse_company_uuid').value,
|
||||
"X-HTTP-Method-Override": "PUT"
|
||||
};
|
||||
|
||||
$.post('/api/v1/office/travel-reimburse/', payload)
|
||||
.done(function (response) {
|
||||
|
||||
if (response.error) {
|
||||
alert(response.error);
|
||||
return;
|
||||
}
|
||||
|
||||
calendar.refetchEvents();
|
||||
|
||||
// close modal
|
||||
bootstrap.Modal.getInstance(
|
||||
document.getElementById('travelModal')
|
||||
).hide();
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
console.error(xhr.responseText);
|
||||
alert('Something went wrong');
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('deleteTravelBtn').addEventListener('click', function () {
|
||||
|
||||
const payload = {
|
||||
reimburse_uuid: document.getElementById('reimburse_uuid').value,
|
||||
"X-HTTP-Method-Override": "DELETE"
|
||||
};
|
||||
|
||||
$.post('/api/v1/office/travel-reimburse/', payload)
|
||||
.done(function (response) {
|
||||
|
||||
if (response.error) {
|
||||
alert(response.error);
|
||||
return;
|
||||
}
|
||||
|
||||
calendar.refetchEvents();
|
||||
|
||||
// close modal
|
||||
bootstrap.Modal.getInstance(
|
||||
document.getElementById('travelModal')
|
||||
).hide();
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
console.error(xhr.responseText);
|
||||
alert('Something went wrong');
|
||||
});
|
||||
});
|
||||
|
||||
function createTravelReimburse(date) {
|
||||
|
||||
const payload = {
|
||||
departure_postcode: document.getElementById('new_departure_postcode').value,
|
||||
destination_postcode: document.getElementById('new_destination_postcode').value,
|
||||
travel_distance: document.getElementById('new_travel_distance').value,
|
||||
homework: Number(document.getElementById('new_homework').checked),
|
||||
office_travelreimburse_company_uuid: document.getElementById('new_office_travelreimburse_company_uuid').value,
|
||||
travel_date: date,
|
||||
"X-HTTP-Method-Override": "POST"
|
||||
};
|
||||
|
||||
$.post('/api/v1/office/travel-reimburse/', payload)
|
||||
.done(function (response) {
|
||||
|
||||
if (response.error) {
|
||||
alert(response.error);
|
||||
return;
|
||||
}
|
||||
|
||||
// refresh calendar
|
||||
calendar.refetchEvents();
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
console.error(xhr.responseText);
|
||||
alert('Something went wrong');
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
const form = document.getElementById('defaultForNew');
|
||||
|
||||
const fields = [
|
||||
'new_departure_postcode',
|
||||
'new_destination_postcode',
|
||||
'new_travel_distance',
|
||||
'new_homework',
|
||||
'new_office_travelreimburse_company_uuid'
|
||||
];
|
||||
|
||||
// Load defaults
|
||||
const defaults = JSON.parse(localStorage.getItem('travelDefaults') || '{}');
|
||||
|
||||
fields.forEach(id => {
|
||||
const element = document.getElementById(id);
|
||||
|
||||
if (!element || defaults[id] === undefined) return;
|
||||
|
||||
if (element.type === 'checkbox') {
|
||||
element.checked = !!defaults[id];
|
||||
} else {
|
||||
element.value = defaults[id];
|
||||
}
|
||||
});
|
||||
|
||||
// Save defaults
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const defaults = {};
|
||||
|
||||
fields.forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
|
||||
if (!el) return;
|
||||
|
||||
if (el.type === 'checkbox') {
|
||||
defaults[id] = el.checked ? 1 : 0;
|
||||
} else {
|
||||
defaults[id] = el.value;
|
||||
}
|
||||
});
|
||||
|
||||
localStorage.setItem('travelDefaults', JSON.stringify(defaults));
|
||||
|
||||
Swal.fire({
|
||||
background: background,
|
||||
color: color,
|
||||
icon: 'success',
|
||||
title: __('default_saved'),
|
||||
text: __('default_have_been_saved'),
|
||||
timer: 700,
|
||||
showConfirmButton: false
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user