timesheet export (#317)
* remove autocomplete for begin and end * fix minute display in timesheet-edit form * toolbar form label in separate row * added export action
This commit is contained in:
@@ -18,7 +18,7 @@ matrix:
|
|||||||
- php: 7.2
|
- php: 7.2
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then phpenv config-rm xdebug.ini; fi
|
- phpenv config-rm xdebug.ini
|
||||||
- composer self-update
|
- composer self-update
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
|||||||
@@ -51,12 +51,18 @@ $(function() {
|
|||||||
$('input[data-datepicker="on"]').daterangepicker({
|
$('input[data-datepicker="on"]').daterangepicker({
|
||||||
singleDatePicker: true,
|
singleDatePicker: true,
|
||||||
showDropdowns: true,
|
showDropdowns: true,
|
||||||
|
autoUpdateInput: false,
|
||||||
locale: {
|
locale: {
|
||||||
format: "YYYY-MM-DD",
|
format: "YYYY-MM-DD",
|
||||||
firstDay: 1
|
firstDay: 1
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('input[data-datepicker="on"]').on('apply.daterangepicker', function(ev, picker) {
|
||||||
|
$(this).val(picker.startDate.format('YYYY-MM-DD'));
|
||||||
|
$(this).trigger("change");
|
||||||
|
});
|
||||||
|
|
||||||
$('input[data-datetimepicker="on"]').daterangepicker({
|
$('input[data-datetimepicker="on"]').daterangepicker({
|
||||||
singleDatePicker: true,
|
singleDatePicker: true,
|
||||||
timePicker: true,
|
timePicker: true,
|
||||||
@@ -69,8 +75,10 @@ $(function() {
|
|||||||
firstDay: 1
|
firstDay: 1
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('input[data-datetimepicker="on"]').on('apply.daterangepicker', function(ev, picker) {
|
$('input[data-datetimepicker="on"]').on('apply.daterangepicker', function(ev, picker) {
|
||||||
$(this).val(picker.startDate.format('YYYY-MM-DD HH:mm'));
|
$(this).val(picker.startDate.format('YYYY-MM-DD HH:mm'));
|
||||||
|
$(this).trigger("change");
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -98,8 +106,6 @@ $(function() {
|
|||||||
|
|
||||||
// default values
|
// default values
|
||||||
$.kimai.defaults = {
|
$.kimai.defaults = {
|
||||||
baseUrl: '/',
|
|
||||||
imagePath: '/images',
|
|
||||||
confirmDelete: 'Really delete?',
|
confirmDelete: 'Really delete?',
|
||||||
alertSuccessAutoHide: 5000
|
alertSuccessAutoHide: 5000
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
float: right;
|
float: right;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Right sidebar with additional tabs for personal settings and "About Kimai" section */
|
/* Right sidebar with additional tabs for personal settings and "About Kimai" section */
|
||||||
.control-sidebar {
|
.control-sidebar {
|
||||||
select {
|
select {
|
||||||
@@ -23,7 +24,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* The filter form available on most pages above the datatable */
|
|
||||||
.toolbar form {
|
/* The filter form is available on most pages above the datatable */
|
||||||
font-size: $font-size-base;
|
@media (min-width: 768px) {
|
||||||
|
.toolbar {
|
||||||
|
form.navbar-form {
|
||||||
|
font-size: $font-size-base;
|
||||||
|
.form-control {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
<directory suffix=".php">assets/</directory>
|
<directory suffix=".php">assets/</directory>
|
||||||
<directory suffix=".php">bin/</directory>
|
<directory suffix=".php">bin/</directory>
|
||||||
<directory suffix=".php">config/</directory>
|
<directory suffix=".php">config/</directory>
|
||||||
|
<directory suffix=".php">node_modules/</directory>
|
||||||
<directory suffix=".php">public/</directory>
|
<directory suffix=".php">public/</directory>
|
||||||
<directory suffix=".php">tests/</directory>
|
<directory suffix=".php">tests/</directory>
|
||||||
<directory suffix=".php">translations/</directory>
|
<directory suffix=".php">translations/</directory>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"build/app.js": "/build/app.js?ba9725f5267413a18701",
|
"build/app.js": "/build/app.js?227afb1055d55a1cbd52",
|
||||||
"build/app.css": "/build/app.css?b30340309e980dc272f3ca6d1ff64aa2",
|
"build/app.css": "/build/app.css?6501689dff217d14b179e3049295343e",
|
||||||
"build/fonts/fa-regular-400.woff": "/build/fonts/fa-regular-400.woff?d9e29124",
|
"build/fonts/fa-regular-400.woff": "/build/fonts/fa-regular-400.woff?d9e29124",
|
||||||
"build/images/blue@2x.png": "/build/images/blue@2x.png?2694acfd",
|
"build/images/blue@2x.png": "/build/images/blue@2x.png?2694acfd",
|
||||||
"build/fonts/fa-solid-900.woff2": "/build/fonts/fa-solid-900.woff2?e8a92a29",
|
"build/fonts/fa-solid-900.woff2": "/build/fonts/fa-solid-900.woff2?e8a92a29",
|
||||||
|
|||||||
@@ -60,6 +60,12 @@ class TimesheetController extends AbstractController
|
|||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
/** @var TimesheetQuery $query */
|
/** @var TimesheetQuery $query */
|
||||||
$query = $form->getData();
|
$query = $form->getData();
|
||||||
|
if (null !== $query->getBegin()) {
|
||||||
|
$query->getBegin()->setTime(0, 0, 0);
|
||||||
|
}
|
||||||
|
if (null !== $query->getEnd()) {
|
||||||
|
$query->getEnd()->setTime(23, 59, 59);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @var $entries Pagerfanta */
|
/* @var $entries Pagerfanta */
|
||||||
@@ -118,7 +124,7 @@ class TimesheetController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* The route to delete an existing entry.
|
* The route to delete an existing entry.
|
||||||
*
|
*
|
||||||
* @Route(path="/{id}/delete", name="admin_timesheet_delete", methods={"GET", "POST"})
|
* @Route(path="/{id}/delete", defaults={"page": 1}, name="admin_timesheet_delete", methods={"GET", "POST"})
|
||||||
* @Security("is_granted('delete', entry)")
|
* @Security("is_granted('delete', entry)")
|
||||||
*
|
*
|
||||||
* @param Timesheet $entry
|
* @param Timesheet $entry
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ class TimesheetController extends AbstractController
|
|||||||
* @Route(path="/", defaults={"page": 1}, name="timesheet", methods={"GET"})
|
* @Route(path="/", defaults={"page": 1}, name="timesheet", methods={"GET"})
|
||||||
* @Route(path="/page/{page}", requirements={"page": "[1-9]\d*"}, name="timesheet_paginated", methods={"GET"})
|
* @Route(path="/page/{page}", requirements={"page": "[1-9]\d*"}, name="timesheet_paginated", methods={"GET"})
|
||||||
* @Cache(smaxage="10")
|
* @Cache(smaxage="10")
|
||||||
|
*
|
||||||
|
* @param int $page
|
||||||
|
* @param Request $request
|
||||||
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
*/
|
*/
|
||||||
public function indexAction($page, Request $request)
|
public function indexAction($page, Request $request)
|
||||||
{
|
{
|
||||||
@@ -53,6 +57,12 @@ class TimesheetController extends AbstractController
|
|||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
/** @var TimesheetQuery $query */
|
/** @var TimesheetQuery $query */
|
||||||
$query = $form->getData();
|
$query = $form->getData();
|
||||||
|
if (null !== $query->getBegin()) {
|
||||||
|
$query->getBegin()->setTime(0, 0, 0);
|
||||||
|
}
|
||||||
|
if (null !== $query->getEnd()) {
|
||||||
|
$query->getEnd()->setTime(23, 59, 59);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$query->setUser($this->getUser());
|
$query->setUser($this->getUser());
|
||||||
@@ -69,7 +79,41 @@ class TimesheetController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "main button and flyout" for displaying (and stopping) active entries.
|
* @Route(path="/export", name="timesheet_export", methods={"GET"})
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
|
*/
|
||||||
|
public function exportAction(Request $request)
|
||||||
|
{
|
||||||
|
$query = new TimesheetQuery();
|
||||||
|
|
||||||
|
$form = $this->getToolbarForm($query);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
/** @var TimesheetQuery $query */
|
||||||
|
$query = $form->getData();
|
||||||
|
if (null !== $query->getBegin()) {
|
||||||
|
$query->getBegin()->setTime(0, 0, 0);
|
||||||
|
}
|
||||||
|
if (null !== $query->getEnd()) {
|
||||||
|
$query->getEnd()->setTime(23, 59, 59);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->setUser($this->getUser());
|
||||||
|
|
||||||
|
/* @var $entries Pagerfanta */
|
||||||
|
$entries = $this->getRepository()->findByQuery($query);
|
||||||
|
|
||||||
|
return $this->render('timesheet/export.html.twig', [
|
||||||
|
'entries' => $entries,
|
||||||
|
'query' => $query,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "main button and fly-out" for displaying (and stopping) active entries.
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
*/
|
*/
|
||||||
@@ -155,7 +199,7 @@ class TimesheetController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* The route to delete an existing entry.
|
* The route to delete an existing entry.
|
||||||
*
|
*
|
||||||
* @Route(path="/{id}/delete", name="timesheet_delete", methods={"GET", "POST"})
|
* @Route(path="/{id}/delete", defaults={"page": 1}, name="timesheet_delete", methods={"GET", "POST"})
|
||||||
* @Security("is_granted('delete', entry)")
|
* @Security("is_granted('delete', entry)")
|
||||||
*
|
*
|
||||||
* @param Timesheet $entry
|
* @param Timesheet $entry
|
||||||
|
|||||||
@@ -92,8 +92,6 @@ trait TimesheetControllerTrait
|
|||||||
$record->setEnd($end);
|
$record->setEnd($end);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO validate that end is not before begin
|
|
||||||
|
|
||||||
$entityManager = $this->getDoctrine()->getManager();
|
$entityManager = $this->getDoctrine()->getManager();
|
||||||
$entityManager->persist($entry);
|
$entityManager->persist($entry);
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
@@ -170,8 +168,6 @@ trait TimesheetControllerTrait
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO validate that end is not before begin
|
|
||||||
|
|
||||||
$entityManager = $this->getDoctrine()->getManager();
|
$entityManager = $this->getDoctrine()->getManager();
|
||||||
$entityManager->persist($entry);
|
$entityManager->persist($entry);
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,14 @@ class Activity
|
|||||||
*/
|
*/
|
||||||
private $hourlyRate = null;
|
private $hourlyRate = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Timesheet[]
|
* @return Timesheet[]
|
||||||
*/
|
*/
|
||||||
@@ -113,8 +121,6 @@ class Activity
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set name
|
|
||||||
*
|
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @return Activity
|
* @return Activity
|
||||||
*/
|
*/
|
||||||
@@ -126,8 +132,6 @@ class Activity
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get name
|
|
||||||
*
|
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getName()
|
public function getName()
|
||||||
@@ -136,8 +140,6 @@ class Activity
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set comment
|
|
||||||
*
|
|
||||||
* @param string $comment
|
* @param string $comment
|
||||||
* @return Activity
|
* @return Activity
|
||||||
*/
|
*/
|
||||||
@@ -149,8 +151,6 @@ class Activity
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get comment
|
|
||||||
*
|
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getComment()
|
public function getComment()
|
||||||
@@ -159,10 +159,7 @@ class Activity
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set visible
|
|
||||||
*
|
|
||||||
* @param bool $visible
|
* @param bool $visible
|
||||||
*
|
|
||||||
* @return Activity
|
* @return Activity
|
||||||
*/
|
*/
|
||||||
public function setVisible($visible)
|
public function setVisible($visible)
|
||||||
@@ -173,8 +170,6 @@ class Activity
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get visible
|
|
||||||
*
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function getVisible()
|
public function getVisible()
|
||||||
@@ -182,16 +177,6 @@ class Activity
|
|||||||
return $this->visible;
|
return $this->visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get activity id
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getId()
|
|
||||||
{
|
|
||||||
return $this->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return float
|
* @return float
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -102,8 +102,6 @@ class Project
|
|||||||
private $hourlyRate = null;
|
private $hourlyRate = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get projectid
|
|
||||||
*
|
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function getId()
|
public function getId()
|
||||||
@@ -144,8 +142,6 @@ class Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get name
|
|
||||||
*
|
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getName()
|
public function getName()
|
||||||
@@ -154,8 +150,6 @@ class Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set comment
|
|
||||||
*
|
|
||||||
* @param string $comment
|
* @param string $comment
|
||||||
* @return Project
|
* @return Project
|
||||||
*/
|
*/
|
||||||
@@ -167,8 +161,6 @@ class Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get comment
|
|
||||||
*
|
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getComment()
|
public function getComment()
|
||||||
@@ -177,8 +169,6 @@ class Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set visible
|
|
||||||
*
|
|
||||||
* @param bool $visible
|
* @param bool $visible
|
||||||
* @return Project
|
* @return Project
|
||||||
*/
|
*/
|
||||||
@@ -190,8 +180,6 @@ class Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get visible
|
|
||||||
*
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function getVisible()
|
public function getVisible()
|
||||||
@@ -200,8 +188,6 @@ class Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set budget
|
|
||||||
*
|
|
||||||
* @param float $budget
|
* @param float $budget
|
||||||
* @return Project
|
* @return Project
|
||||||
*/
|
*/
|
||||||
@@ -213,8 +199,6 @@ class Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get budget
|
|
||||||
*
|
|
||||||
* @return float
|
* @return float
|
||||||
*/
|
*/
|
||||||
public function getBudget()
|
public function getBudget()
|
||||||
@@ -242,7 +226,7 @@ class Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function getOrderNumber(): ?string
|
public function getOrderNumber(): ?string
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,9 +44,9 @@ class TimesheetEditForm extends AbstractType
|
|||||||
'label' => 'label.begin',
|
'label' => 'label.begin',
|
||||||
'widget' => 'single_text',
|
'widget' => 'single_text',
|
||||||
'html5' => false,
|
'html5' => false,
|
||||||
'format' => 'yyyy-MM-dd H:m',
|
'format' => 'yyyy-MM-dd HH:mm',
|
||||||
'with_seconds' => false,
|
'with_seconds' => false,
|
||||||
'attr' => ['data-datetimepicker' => 'on'],
|
'attr' => ['autocomplete' => 'off', 'data-datetimepicker' => 'on'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,9 +58,9 @@ class TimesheetEditForm extends AbstractType
|
|||||||
'widget' => 'single_text',
|
'widget' => 'single_text',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'html5' => false,
|
'html5' => false,
|
||||||
'format' => 'yyyy-MM-dd H:m',
|
'format' => 'yyyy-MM-dd HH:mm',
|
||||||
'with_seconds' => false,
|
'with_seconds' => false,
|
||||||
'attr' => ['data-datetimepicker' => 'on'],
|
'attr' => ['autocomplete' => 'off', 'data-datetimepicker' => 'on'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,8 @@ abstract class AbstractToolbarForm extends AbstractType
|
|||||||
'widget' => 'single_text',
|
'widget' => 'single_text',
|
||||||
'html5' => false,
|
'html5' => false,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'attr' => ['data-datepicker' => 'on'],
|
'format' => 'yyyy-MM-dd',
|
||||||
|
'attr' => ['autocomplete' => 'off', 'data-datepicker' => 'on'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +125,8 @@ abstract class AbstractToolbarForm extends AbstractType
|
|||||||
'widget' => 'single_text',
|
'widget' => 'single_text',
|
||||||
'html5' => false,
|
'html5' => false,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'attr' => ['data-datepicker' => 'on'],
|
'format' => 'yyyy-MM-dd',
|
||||||
|
'attr' => ['autocomplete' => 'off', 'data-datepicker' => 'on'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ class TimesheetAdminToolbarForm extends TimesheetToolbarForm
|
|||||||
$this->addTimesheetStateChoice($builder);
|
$this->addTimesheetStateChoice($builder);
|
||||||
$this->addPageSizeChoice($builder);
|
$this->addPageSizeChoice($builder);
|
||||||
$this->addUserChoice($builder);
|
$this->addUserChoice($builder);
|
||||||
|
$this->addStartDateChoice($builder);
|
||||||
|
$this->addEndDateChoice($builder);
|
||||||
$this->addCustomerChoice($builder);
|
$this->addCustomerChoice($builder);
|
||||||
$this->addProjectChoice($builder);
|
$this->addProjectChoice($builder);
|
||||||
$this->addActivityChoice($builder);
|
$this->addActivityChoice($builder);
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ class TimesheetToolbarForm extends AbstractToolbarForm
|
|||||||
{
|
{
|
||||||
$this->addTimesheetStateChoice($builder);
|
$this->addTimesheetStateChoice($builder);
|
||||||
$this->addPageSizeChoice($builder);
|
$this->addPageSizeChoice($builder);
|
||||||
|
$this->addStartDateChoice($builder);
|
||||||
|
$this->addEndDateChoice($builder);
|
||||||
$this->addCustomerChoice($builder);
|
$this->addCustomerChoice($builder);
|
||||||
$this->addProjectChoice($builder);
|
$this->addProjectChoice($builder);
|
||||||
$this->addActivityChoice($builder);
|
$this->addActivityChoice($builder);
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ class Extensions extends \Twig_Extension
|
|||||||
'create' => 'far fa-plus-square',
|
'create' => 'far fa-plus-square',
|
||||||
'dashboard' => 'fas fa-tachometer-alt',
|
'dashboard' => 'fas fa-tachometer-alt',
|
||||||
'delete' => 'far fa-trash-alt',
|
'delete' => 'far fa-trash-alt',
|
||||||
|
'download' => 'fas fa-download',
|
||||||
|
'duration' => 'far fa-hourglass',
|
||||||
'edit' => 'far fa-edit',
|
'edit' => 'far fa-edit',
|
||||||
'filter' => 'fas fa-filter',
|
'filter' => 'fas fa-filter',
|
||||||
'help' => 'far fa-question-circle',
|
'help' => 'far fa-question-circle',
|
||||||
@@ -69,6 +71,7 @@ class Extensions extends \Twig_Extension
|
|||||||
'list' => 'fas fa-list',
|
'list' => 'fas fa-list',
|
||||||
'logout' => 'fas fa-sign-out-alt',
|
'logout' => 'fas fa-sign-out-alt',
|
||||||
'manual' => 'fas fa-book',
|
'manual' => 'fas fa-book',
|
||||||
|
'money' => 'far fa-money-bill-alt',
|
||||||
'print' => 'fas fa-print',
|
'print' => 'fas fa-print',
|
||||||
'project' => 'fas fa-project-diagram',
|
'project' => 'fas fa-project-diagram',
|
||||||
'repeat' => 'fas fa-redo-alt',
|
'repeat' => 'fas fa-redo-alt',
|
||||||
@@ -80,8 +83,6 @@ class Extensions extends \Twig_Extension
|
|||||||
'trash' => 'far fa-trash-alt',
|
'trash' => 'far fa-trash-alt',
|
||||||
'user' => 'fas fa-user',
|
'user' => 'fas fa-user',
|
||||||
'visibility' => 'far fa-eye',
|
'visibility' => 'far fa-eye',
|
||||||
'money' => 'far fa-money-bill-alt',
|
|
||||||
'duration' => 'far fa-hourglass',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -94,7 +94,7 @@
|
|||||||
{# we do not call parent() as we use a custom built for the frontend assets and don't want the default <script> #}
|
{# we do not call parent() as we use a custom built for the frontend assets and don't want the default <script> #}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$.kimai.init({imagePath: '{{ asset('images') }}', confirmDelete: '{{ 'confirm.delete'|trans }}'});
|
$.kimai.init({confirmDelete: '{{ 'confirm.delete'|trans }}'});
|
||||||
//$.kimai.pauseRecord('li.messages-menu ul.menu li');
|
//$.kimai.pauseRecord('li.messages-menu ul.menu li');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -23,5 +23,3 @@
|
|||||||
})|raw }}
|
})|raw }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{#% block form_label %}{% endblock %#}
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascripts %}
|
{% block javascripts %}
|
||||||
|
{{ parent() }}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$( "#print-invoice-button" ).click(function() {
|
$( "#print-invoice-button" ).click(function() {
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
{% elseif '#modal' in url %}
|
{% elseif '#modal' in url %}
|
||||||
data-toggle="modal" data-target="{{ url }}"
|
data-toggle="modal" data-target="{{ url }}"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if 'onclick:' in url %}
|
||||||
|
onclick="{{ url|replace({'onclick:': ''}) }}"
|
||||||
|
{% endif %}
|
||||||
><i class="{{ icon|icon(icon) }}"></i></a>
|
><i class="{{ icon|icon(icon) }}"></i></a>
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
70
templates/timesheet/export.html.twig
Normal file
70
templates/timesheet/export.html.twig
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
{% import "macros/widgets.html.twig" as widgets %}
|
||||||
|
{% extends 'invoice/layout.html.twig' %}
|
||||||
|
|
||||||
|
{% block invoice %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<h2 class="page-header">
|
||||||
|
<span contenteditable="true">
|
||||||
|
{% if query.begin is not empty and query.end is not empty %}
|
||||||
|
{% if query.begin|date('m') != query.end|date('m') or query.begin|date('Y') != query.end|date('Y') %}
|
||||||
|
{{ query.begin|date_short }} - {{ query.end|date_short }}:
|
||||||
|
{% elseif query.end is not empty %}
|
||||||
|
{{ query.end|month_name|trans }} {{ query.end|date('Y') }}:
|
||||||
|
{% elseif query.begin is not empty %}
|
||||||
|
{{ query.begin|month_name|trans }} {{ query.begin|date('Y') }}:
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{{ widgets.username(query.user) }}
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{ 'label.date'|trans }}</th>
|
||||||
|
<th>{{ 'label.description'|trans }}</th>
|
||||||
|
<th>{{ 'label.hours'|trans }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% set timeWorked = 0 %}
|
||||||
|
{% for entry in entries %}
|
||||||
|
{% set timeWorked = timeWorked + entry.duration %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ entry.begin|date_short }}</td>
|
||||||
|
<td>
|
||||||
|
{% if entry.description is not empty %}
|
||||||
|
<div>
|
||||||
|
{{ entry.description|desc2html }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<span class="small">
|
||||||
|
{{ 'label.activity'|trans }}: {{ entry.activity.name }} |
|
||||||
|
{{ 'label.project'|trans }}: {{ entry.activity.project.name }} |
|
||||||
|
{{ 'label.customer'|trans }}: {{ entry.activity.project.customer.name }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>{{ entry.duration|duration }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>{{ 'invoice.total_working_time'|trans }}</th>
|
||||||
|
<th>{{ timeWorked|duration }}</th>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block print_button %}{% endblock %}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{% block page_title %}{{ 'timesheet.title'|trans }}{% endblock %}
|
{% block page_title %}{{ 'timesheet.title'|trans }}{% endblock %}
|
||||||
{% block page_subtitle %}{{ 'timesheet.subtitle'|trans }}{% endblock %}
|
{% block page_subtitle %}{{ 'timesheet.subtitle'|trans }}{% endblock %}
|
||||||
{% block page_actions %}{{ widgets.page_actions({'filter': '#collapseTimesheet', 'visibility': '#modal_timesheet', 'calendar': path('calendar'), 'create': path('timesheet_create')}) }}{% endblock %}
|
{% block page_actions %}{{ widgets.page_actions({'filter': '#collapseTimesheet', 'download': 'onclick:return exportTimesheet()', 'visibility': '#modal_timesheet', 'calendar': path('calendar'), 'create': path('timesheet_create')}) }}{% endblock %}
|
||||||
|
|
||||||
{% block main_before %}
|
{% block main_before %}
|
||||||
{{ toolbar.toolbar(toolbarForm, 'collapseTimesheet') }}
|
{{ toolbar.toolbar(toolbarForm, 'collapseTimesheet') }}
|
||||||
@@ -81,3 +81,17 @@
|
|||||||
{{ tables.data_table_footer(entries, 'timesheet_paginated') }}
|
{{ tables.data_table_footer(entries, 'timesheet_paginated') }}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block javascripts %}
|
||||||
|
{{ parent() }}
|
||||||
|
<script type="text/javascript">
|
||||||
|
function exportTimesheet() {
|
||||||
|
var form = $("div.toolbar form.navbar-form");
|
||||||
|
var prevAction = form.attr('action');
|
||||||
|
form.attr('target', '_blank').attr('action', '{{ path('timesheet_export') }}');
|
||||||
|
form.submit();
|
||||||
|
form.removeAttr('target').attr('action', prevAction);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -9,8 +9,10 @@
|
|||||||
|
|
||||||
namespace App\Tests\Controller\Admin;
|
namespace App\Tests\Controller\Admin;
|
||||||
|
|
||||||
|
use App\Entity\Timesheet;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Tests\Controller\ControllerBaseTest;
|
use App\Tests\Controller\ControllerBaseTest;
|
||||||
|
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @coversDefaultClass \App\Controller\Admin\TimesheetController
|
* @coversDefaultClass \App\Controller\Admin\TimesheetController
|
||||||
@@ -29,5 +31,111 @@ class TimesheetControllerTest extends ControllerBaseTest
|
|||||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||||
$this->assertAccessIsGranted($client, '/team/timesheet/');
|
$this->assertAccessIsGranted($client, '/team/timesheet/');
|
||||||
$this->assertHasDataTable($client);
|
$this->assertHasDataTable($client);
|
||||||
|
|
||||||
|
$result = $client->getCrawler()->filter('div.breadcrumb div.box-tools div.btn-group a.btn');
|
||||||
|
$this->assertEquals(3, count($result));
|
||||||
|
|
||||||
|
foreach ($result as $item) {
|
||||||
|
$this->assertEquals('btn btn-default', $item->getAttribute('class'));
|
||||||
|
$this->assertEquals('i', $item->firstChild->tagName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIndexActionWithQuery()
|
||||||
|
{
|
||||||
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||||
|
|
||||||
|
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
|
||||||
|
$fixture = new TimesheetFixtures();
|
||||||
|
$fixture->setAmount(10);
|
||||||
|
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
|
||||||
|
$fixture->setStartDate(new \DateTime('-10 days'));
|
||||||
|
$this->importFixture($em, $fixture);
|
||||||
|
|
||||||
|
$this->request($client, '/team/timesheet/');
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
|
$form = $client->getCrawler()->filter('form.navbar-form')->form();
|
||||||
|
$client->submit($form, [
|
||||||
|
'state' => 1,
|
||||||
|
'user' => 1,
|
||||||
|
'pageSize' => 25,
|
||||||
|
'begin' => (new \DateTime('-10 days'))->format('Y-m-d'),
|
||||||
|
'end' => (new \DateTime())->format('Y-m-d'),
|
||||||
|
'customer' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
$this->assertHasDataTable($client);
|
||||||
|
|
||||||
|
// TODO more assertions
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreateAction()
|
||||||
|
{
|
||||||
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||||
|
$this->request($client, '/team/timesheet/create');
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
|
$form = $client->getCrawler()->filter('form[name=timesheet_edit_form]')->form();
|
||||||
|
$client->submit($form, [
|
||||||
|
'timesheet_edit_form' => [
|
||||||
|
'description' => 'Testing is fun!'
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertIsRedirect($client, $this->createUrl('/team/timesheet/'));
|
||||||
|
$client->followRedirect();
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
$this->assertHasFlashSuccess($client);
|
||||||
|
|
||||||
|
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
|
||||||
|
/** @var Timesheet $timesheet */
|
||||||
|
$timesheet = $em->getRepository(Timesheet::class)->find(1);
|
||||||
|
$this->assertInstanceOf(\DateTime::class, $timesheet->getBegin());
|
||||||
|
$this->assertNull($timesheet->getEnd());
|
||||||
|
$this->assertEquals('Testing is fun!', $timesheet->getDescription());
|
||||||
|
$this->assertEquals(0, $timesheet->getRate());
|
||||||
|
$this->assertNull($timesheet->getHourlyRate());
|
||||||
|
$this->assertNull($timesheet->getFixedRate());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDeleteActionIsNotAllowedForTeamlead()
|
||||||
|
{
|
||||||
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||||
|
|
||||||
|
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
|
||||||
|
$fixture = new TimesheetFixtures();
|
||||||
|
$fixture->setAmount(10);
|
||||||
|
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
|
||||||
|
$fixture->setStartDate('2017-05-01');
|
||||||
|
$this->importFixture($em, $fixture);
|
||||||
|
|
||||||
|
$this->request($client, '/team/timesheet/1/delete');
|
||||||
|
$this->assertFalse($client->getResponse()->isSuccessful());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDeleteAction()
|
||||||
|
{
|
||||||
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||||
|
|
||||||
|
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
|
||||||
|
$fixture = new TimesheetFixtures();
|
||||||
|
$fixture->setAmount(10);
|
||||||
|
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
|
||||||
|
$fixture->setStartDate('2017-05-01');
|
||||||
|
$this->importFixture($em, $fixture);
|
||||||
|
|
||||||
|
$this->request($client, '/team/timesheet/1/edit');
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
|
$this->request($client, '/team/timesheet/1/delete');
|
||||||
|
$this->assertIsRedirect($client, $this->createUrl('/team/timesheet/page/1'));
|
||||||
|
$client->followRedirect();
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
$this->assertHasFlashSuccess($client);
|
||||||
|
|
||||||
|
$this->request($client, '/team/timesheet/1/edit');
|
||||||
|
$this->assertFalse($client->getResponse()->isSuccessful());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,76 @@ class TimesheetControllerTest extends ControllerBaseTest
|
|||||||
$this->request($client, '/timesheet/');
|
$this->request($client, '/timesheet/');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$this->assertHasDataTable($client);
|
$this->assertHasDataTable($client);
|
||||||
|
|
||||||
|
$result = $client->getCrawler()->filter('div.breadcrumb div.box-tools div.btn-group a.btn');
|
||||||
|
$this->assertEquals(5, count($result));
|
||||||
|
|
||||||
|
foreach ($result as $item) {
|
||||||
|
$this->assertEquals('btn btn-default', $item->getAttribute('class'));
|
||||||
|
$this->assertEquals('i', $item->firstChild->tagName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIndexActionWithQuery()
|
||||||
|
{
|
||||||
|
$client = $this->getClientForAuthenticatedUser();
|
||||||
|
|
||||||
|
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
|
||||||
|
$fixture = new TimesheetFixtures();
|
||||||
|
$fixture->setAmount(5);
|
||||||
|
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
|
||||||
|
$fixture->setStartDate(new \DateTime('-10 days'));
|
||||||
|
$this->importFixture($em, $fixture);
|
||||||
|
|
||||||
|
$this->request($client, '/timesheet/');
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
|
$form = $client->getCrawler()->filter('form.navbar-form')->form();
|
||||||
|
$client->submit($form, [
|
||||||
|
'state' => 1,
|
||||||
|
'pageSize' => 25,
|
||||||
|
'begin' => (new \DateTime('-10 days'))->format('Y-m-d'),
|
||||||
|
'end' => (new \DateTime())->format('Y-m-d'),
|
||||||
|
'customer' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
$this->assertHasDataTable($client);
|
||||||
|
|
||||||
|
// TODO more assertions
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExportAction()
|
||||||
|
{
|
||||||
|
$client = $this->getClientForAuthenticatedUser();
|
||||||
|
|
||||||
|
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
|
||||||
|
$fixture = new TimesheetFixtures();
|
||||||
|
$fixture->setAmount(5);
|
||||||
|
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
|
||||||
|
$fixture->setStartDate(new \DateTime('-10 days'));
|
||||||
|
$this->importFixture($em, $fixture);
|
||||||
|
|
||||||
|
$this->request($client, '/timesheet/');
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
|
$form = $client->getCrawler()->filter('form.navbar-form')->form();
|
||||||
|
$form->getFormNode()->setAttribute('action', $this->createUrl('/timesheet/export'));
|
||||||
|
$client->submit($form, [
|
||||||
|
'state' => 1,
|
||||||
|
'pageSize' => 25,
|
||||||
|
'begin' => (new \DateTime('-10 days'))->format('Y-m-d'),
|
||||||
|
'end' => (new \DateTime())->format('Y-m-d'),
|
||||||
|
'customer' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
|
$node = $client->getCrawler()->filter('body');
|
||||||
|
$this->assertEquals('invoice_print', $node->getNode(0)->getAttribute('class'));
|
||||||
|
|
||||||
|
$result = $node->filter('section.invoice table.table tbody tr');
|
||||||
|
$this->assertEquals(5, count($result));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCreateAction()
|
public function testCreateAction()
|
||||||
@@ -61,6 +131,30 @@ class TimesheetControllerTest extends ControllerBaseTest
|
|||||||
$this->assertNull($timesheet->getFixedRate());
|
$this->assertNull($timesheet->getFixedRate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testDeleteAction()
|
||||||
|
{
|
||||||
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||||
|
|
||||||
|
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
|
||||||
|
$fixture = new TimesheetFixtures();
|
||||||
|
$fixture->setAmount(10);
|
||||||
|
$fixture->setUser($this->getUserByRole($em, User::ROLE_USER));
|
||||||
|
$fixture->setStartDate('2017-05-01');
|
||||||
|
$this->importFixture($em, $fixture);
|
||||||
|
|
||||||
|
$this->request($client, '/timesheet/1/edit');
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
|
$this->request($client, '/timesheet/1/delete');
|
||||||
|
$this->assertIsRedirect($client, $this->createUrl('/timesheet/page/1'));
|
||||||
|
$client->followRedirect();
|
||||||
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
$this->assertHasFlashSuccess($client);
|
||||||
|
|
||||||
|
$this->request($client, '/timesheet/1/edit');
|
||||||
|
$this->assertFalse($client->getResponse()->isSuccessful());
|
||||||
|
}
|
||||||
|
|
||||||
public function testStartAction()
|
public function testStartAction()
|
||||||
{
|
{
|
||||||
$client = $this->getClientForAuthenticatedUser();
|
$client = $this->getClientForAuthenticatedUser();
|
||||||
@@ -184,15 +278,15 @@ class TimesheetControllerTest extends ControllerBaseTest
|
|||||||
$this->request($client, '/timesheet/1/edit');
|
$this->request($client, '/timesheet/1/edit');
|
||||||
|
|
||||||
$response = $client->getResponse();
|
$response = $client->getResponse();
|
||||||
|
$this->assertTrue($response->isSuccessful());
|
||||||
|
|
||||||
$docuUrl = $this->createUrl('/help/timesheet');
|
$docuUrl = $this->createUrl('/help/timesheet');
|
||||||
$this->assertTrue($response->isSuccessful());
|
|
||||||
$this->assertContains(
|
$this->assertContains(
|
||||||
'<a href="' . $docuUrl . '"><i class="far fa-question-circle"></i></a>',
|
'<a href="' . $docuUrl . '"><i class="far fa-question-circle"></i></a>',
|
||||||
$response->getContent(),
|
$response->getContent(),
|
||||||
'Could not find link to documentation'
|
'Could not find link to documentation'
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO more tests
|
// TODO more assertions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ class ExtensionsTest extends TestCase
|
|||||||
$icons = [
|
$icons = [
|
||||||
'user', 'customer', 'project', 'activity', 'admin', 'invoice', 'timesheet', 'dashboard', 'logout', 'trash',
|
'user', 'customer', 'project', 'activity', 'admin', 'invoice', 'timesheet', 'dashboard', 'logout', 'trash',
|
||||||
'delete', 'repeat', 'edit', 'manual', 'help', 'start', 'start-small', 'stop', 'stop-small', 'filter',
|
'delete', 'repeat', 'edit', 'manual', 'help', 'start', 'start-small', 'stop', 'stop-small', 'filter',
|
||||||
'create', 'list', 'print', 'visibility', 'calendar', 'money', 'duration',
|
'create', 'list', 'print', 'visibility', 'calendar', 'money', 'duration', 'download'
|
||||||
];
|
];
|
||||||
|
|
||||||
// test pre-defined icons
|
// test pre-defined icons
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ For more details check this [dashboard subscriber](../../src/EventSubscriber/Das
|
|||||||
|
|
||||||
## Adding tabs to the "control sidebar"
|
## Adding tabs to the "control sidebar"
|
||||||
|
|
||||||
We use twig globals to render the control sidebar tabs, so adding one is as easy as adding a new config entry:
|
We use the AdminLTE bundle to render the control sidebar tabs, so adding another tab is as easy as adding a new config entry:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
admin_lte:
|
admin_lte:
|
||||||
@@ -179,8 +179,8 @@ admin_lte:
|
|||||||
template: sidebar/home.html.twig
|
template: sidebar/home.html.twig
|
||||||
```
|
```
|
||||||
|
|
||||||
You have to define the `icon` ([read more](theme.md)) to be used and either `controller` action or twig `template`.
|
You have to define the `icon` ([read more](theme.md)) to be used and then either `controller` action or twig `template`.
|
||||||
Both follow the default naming syntax and you can link your bundle here instead of the app controller or templates.
|
Both follow the default naming syntax and you can link your bundle here instead of existing application controller or templates.
|
||||||
You should NOT add them in `config/packages/kimai.yaml` but in your own bundle or the `local.yaml` [config](configurations.md),
|
You should NOT add them in `config/packages/kimai.yaml` but in your own bundle or the `local.yaml` [config](configurations.md),
|
||||||
otherwise they might get lost during an update.
|
otherwise they might get lost during an update.
|
||||||
|
|
||||||
|
|||||||
@@ -61,33 +61,34 @@ with a pre-defined list of icon aliases to guarantee a consistent look.
|
|||||||
|
|
||||||
The pre-defined icons aliases are:
|
The pre-defined icons aliases are:
|
||||||
|
|
||||||
- `user`
|
|
||||||
- `customer`
|
|
||||||
- `project`
|
|
||||||
- `activity`
|
- `activity`
|
||||||
- `admin`
|
- `admin`
|
||||||
- `invoice`
|
- `calendar`
|
||||||
- `timesheet`
|
- `customer`
|
||||||
|
- `create`
|
||||||
- `dashboard`
|
- `dashboard`
|
||||||
- `logout`
|
|
||||||
- `trash`
|
|
||||||
- `delete`
|
- `delete`
|
||||||
- `repeat`
|
- `download`
|
||||||
|
- `duration`
|
||||||
- `edit`
|
- `edit`
|
||||||
- `manual`
|
- `filter`
|
||||||
- `help`
|
- `help`
|
||||||
|
- `invoice`
|
||||||
|
- `list`
|
||||||
|
- `logout`
|
||||||
|
- `manual`
|
||||||
|
- `money`
|
||||||
|
- `print`
|
||||||
|
- `project`
|
||||||
|
- `repeat`
|
||||||
- `start`
|
- `start`
|
||||||
- `start-small`
|
- `start-small`
|
||||||
- `stop`
|
- `stop`
|
||||||
- `stop-small`
|
- `stop-small`
|
||||||
- `filter`
|
- `timesheet`
|
||||||
- `create`
|
- `trash`
|
||||||
- `list`
|
- `user`
|
||||||
- `print`
|
|
||||||
- `visibility`
|
- `visibility`
|
||||||
- `calendar`
|
|
||||||
- `money`
|
|
||||||
- `duration`
|
|
||||||
|
|
||||||
Icon aliases can be used by applying the `icon` filter, e.g.
|
Icon aliases can be used by applying the `icon` filter, e.g.
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Encore
|
|||||||
// the public path used by the web server to access the previous directory
|
// the public path used by the web server to access the previous directory
|
||||||
.setPublicPath('/build/')
|
.setPublicPath('/build/')
|
||||||
|
|
||||||
// delete old files before creating them
|
// empty the outputPath directory before each build
|
||||||
.cleanupOutputBeforeBuild()
|
.cleanupOutputBeforeBuild()
|
||||||
|
|
||||||
// add debug data in development
|
// add debug data in development
|
||||||
@@ -25,9 +25,6 @@ Encore
|
|||||||
// show OS notifications when builds finish/fail
|
// show OS notifications when builds finish/fail
|
||||||
.enableBuildNotifications()
|
.enableBuildNotifications()
|
||||||
|
|
||||||
// empty the outputPath dir before each build
|
|
||||||
.cleanupOutputBeforeBuild()
|
|
||||||
|
|
||||||
// load jquery as Kimai and AdminLTE rely on it
|
// load jquery as Kimai and AdminLTE rely on it
|
||||||
.autoProvidejQuery()
|
.autoProvidejQuery()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user