colors for visual grouping of customer, projects and activities (#751)
This commit is contained in:
@@ -6,34 +6,16 @@
|
||||
*/
|
||||
|
||||
@import "~bootstrap-sass/assets/stylesheets/bootstrap/variables";
|
||||
|
||||
.error-page {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
@media print{
|
||||
.sf-toolbar, .control-sidebar {display: none !important;}
|
||||
}
|
||||
|
||||
@import 'variables';
|
||||
@import 'error-page';
|
||||
@import 'print';
|
||||
@import 'content';
|
||||
@import 'toolbar';
|
||||
|
||||
/* ================================ SIDEBAR ================================ */
|
||||
|
||||
/* Used for the Sidebar UserPanel (which is deactivated right now) */
|
||||
.user-panel>.info {
|
||||
left: 80px;
|
||||
}
|
||||
|
||||
/* ================================ FOOTER ================================ */
|
||||
|
||||
footer.main-footer {
|
||||
padding: 5px;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
@import 'sidebar';
|
||||
@import 'footer';
|
||||
@import 'invoice';
|
||||
@import 'datatable';
|
||||
@import 'datatable-extensions';
|
||||
@import 'calendar';
|
||||
@import 'dashboard';
|
||||
@import 'navbar';
|
||||
|
||||
63
assets/sass/datatable-extensions.scss
Normal file
63
assets/sass/datatable-extensions.scss
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
table.dataTable {
|
||||
/* action column */
|
||||
.actions {
|
||||
width: 90px;
|
||||
li.delete a {
|
||||
color: #dd4b39;
|
||||
}
|
||||
}
|
||||
tr {
|
||||
/* summary row - provided in the users own timesheet table */
|
||||
&.summary td {
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
&.open-edit {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
td {
|
||||
/*
|
||||
Make sure that the action buttons do not line-break if another column takes all available space
|
||||
See https://github.com/twbs/bootstrap/issues/9939#issuecomment-37682952
|
||||
*/
|
||||
.btn-group {
|
||||
display: flex;
|
||||
}
|
||||
.label {
|
||||
font-size: 85%;
|
||||
font-weight: 500;
|
||||
padding: .3em .6em .3em;
|
||||
}
|
||||
span.label-activity,
|
||||
span.label-project,
|
||||
span.label-customer {
|
||||
display: inline-block;
|
||||
.dot {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
background-color: $gray-lte;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
a {
|
||||
color: unset;
|
||||
padding-bottom: 2px;
|
||||
border-bottom: 1px dotted $gray-lte;
|
||||
&:hover {
|
||||
border-bottom: 1px dotted darken($gray-lte, 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
span.label-invisible {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ section.content .overlay,
|
||||
.overlay-wrapper .overlay {
|
||||
z-index: 50;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border-radius: 3px;
|
||||
border-radius: $box-border-radius;
|
||||
}
|
||||
.box .overlay.dark,
|
||||
section.content .overlay.dark,
|
||||
@@ -264,37 +264,3 @@ div.dataTables_scrollHead table.table-bordered {
|
||||
.table-striped.dataTable tbody tr.active:nth-child(odd) th {
|
||||
background-color: #017ebc;
|
||||
}
|
||||
|
||||
table.dataTable {
|
||||
/* action column */
|
||||
.actions {
|
||||
width: 90px;
|
||||
li.delete a {
|
||||
color: #dd4b39;
|
||||
}
|
||||
}
|
||||
/* summary row, like the timesheet table provides */
|
||||
tr {
|
||||
&.summary td {
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
&.open-edit {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
td {
|
||||
/*
|
||||
Make sure that the action buttons do not line-break if another column takes all available space
|
||||
See https://github.com/twbs/bootstrap/issues/9939#issuecomment-37682952
|
||||
*/
|
||||
.btn-group {
|
||||
display: flex;
|
||||
}
|
||||
.label {
|
||||
font-size: 85%;
|
||||
font-weight: 500;
|
||||
padding: .3em .6em .3em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
assets/sass/error-page.scss
Normal file
10
assets/sass/error-page.scss
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
.error-page {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
11
assets/sass/footer.scss
Normal file
11
assets/sass/footer.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
footer.main-footer {
|
||||
padding: 5px;
|
||||
font-size: 80%;
|
||||
}
|
||||
@@ -76,16 +76,3 @@ li.open .ticktac i.running{
|
||||
padding-top: 0px;
|
||||
}
|
||||
*/
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.sidebar-mini.sidebar-collapse .sidebar-menu li.header {
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
display: block !important;
|
||||
margin-top: 16px;
|
||||
opacity: 0.4;
|
||||
span {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
assets/sass/print.scss
Normal file
10
assets/sass/print.scss
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
@media print{
|
||||
.sf-toolbar, .control-sidebar {display: none !important;}
|
||||
}
|
||||
41
assets/sass/sidebar.scss
Normal file
41
assets/sass/sidebar.scss
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/* UserPanel is deactivated right now */
|
||||
/*
|
||||
.user-panel>.info {
|
||||
left: 80px;
|
||||
}
|
||||
*/
|
||||
|
||||
/* Right sidebar with additional tabs for personal settings and "About Kimai" section */
|
||||
/*
|
||||
.control-sidebar {
|
||||
select {
|
||||
color: $input-color;
|
||||
background-color: $input-bg;
|
||||
}
|
||||
div.image {
|
||||
img.img-circle {
|
||||
max-width: 34px;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.sidebar-mini.sidebar-collapse .sidebar-menu li.header {
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
display: block !important;
|
||||
margin-top: 16px;
|
||||
opacity: 0.4;
|
||||
span {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,19 +12,6 @@
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Right sidebar with additional tabs for personal settings and "About Kimai" section */
|
||||
.control-sidebar {
|
||||
select {
|
||||
color: $input-color;
|
||||
background-color: $input-bg;
|
||||
}
|
||||
div.image {
|
||||
img.img-circle {
|
||||
max-width: 34px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-pad {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
126
assets/sass/variables.scss
Normal file
126
assets/sass/variables.scss
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A simple one to one clone of /node_modules/admin-lte/build/less/variables.less
|
||||
*/
|
||||
|
||||
// COLORS
|
||||
// --------------------------------------------------------
|
||||
// Primary
|
||||
//$light-blue: #3c8dbc;
|
||||
// Danger
|
||||
//$red: #dd4b39;
|
||||
// Success
|
||||
//$green: #00a65a;
|
||||
// Info
|
||||
//$aqua: #00c0ef;
|
||||
// Warning
|
||||
//$yellow: #f39c12;
|
||||
//$blue: #0073b7;
|
||||
//$navy: #001F3F;
|
||||
//$teal: #39CCCC;
|
||||
//$olive: #3D9970;
|
||||
//$lime: #01FF70;
|
||||
//$orange: #FF851B;
|
||||
//$fuchsia: #F012BE;
|
||||
//$purple: #605ca8;
|
||||
//$maroon: #D81B60;
|
||||
//$black: #111;
|
||||
$gray-lte: #d2d6de;
|
||||
|
||||
// LAYOUT
|
||||
// --------------------------------------------------------
|
||||
//
|
||||
// Side bar and logo width
|
||||
//$sidebar-width: 230px;
|
||||
//
|
||||
// Link colors (Aka: <a> tags)
|
||||
//$link-color: $light-blue;
|
||||
//$link-hover-color: lighten($link-color, 15%);
|
||||
//
|
||||
// Body background (Affects main content background only)
|
||||
//$body-bg: #ecf0f5; // DEPRECATED
|
||||
//$content-bg: #ecf0f5;
|
||||
//
|
||||
// SIDEBAR SKINS
|
||||
// --------------------------------------------------------
|
||||
//
|
||||
// Dark sidebar
|
||||
//$sidebar-dark-bg: #222d32;
|
||||
//$sidebar-dark-hover-bg: darken($sidebar-dark-bg, 2%);
|
||||
//$sidebar-dark-color: lighten($sidebar-dark-bg, 60%);
|
||||
//$sidebar-dark-hover-color: #fff;
|
||||
//$sidebar-dark-submenu-bg: lighten($sidebar-dark-bg, 5%);
|
||||
//$sidebar-dark-submenu-color: lighten($sidebar-dark-submenu-bg, 40%);
|
||||
//$sidebar-dark-submenu-hover-color: #fff;
|
||||
//
|
||||
// Light sidebar
|
||||
//$sidebar-ligh//t-bg: #f9fafc;
|
||||
//$sidebar-light-hover-bg: lighten(#f0f0f1, 1.5%);
|
||||
//$sidebar-light-color: #444;
|
||||
//$sidebar-light-hover-color: #000;
|
||||
//$sidebar-light-submenu-bg: $sidebar-light-hover-bg;
|
||||
//$sidebar-light-submenu-color: #777;
|
||||
//$sidebar-light-submenu-hover-color: #000;
|
||||
//
|
||||
// sidebar-expanded-on-hover
|
||||
//$sidebar-expanded-shadow: 3px 0 8px rgba(0,0,0,.125);
|
||||
//
|
||||
// CONTROL SIDEBAR
|
||||
// --------------------------------------------------------
|
||||
//$control-sidebar-width: $sidebar-width;
|
||||
//
|
||||
// BOXES
|
||||
// --------------------------------------------------------
|
||||
//$box-border-color: #f4f4f4;
|
||||
$box-border-radius: 3px;
|
||||
//$box-footer-bg: #fff;
|
||||
//$box-boxshadow: 0 1px 1px rgba(0, 0, 0, .1);
|
||||
//$box-padding: 10px;
|
||||
//
|
||||
// Box variants
|
||||
//$box-default-border-top-color: #d2d6de;
|
||||
//
|
||||
// BUTTONS
|
||||
// --------------------------------------------------------
|
||||
//$btn-boxshadow: none;
|
||||
//
|
||||
// PROGRESS BARS
|
||||
// --------------------------------------------------------
|
||||
//$progress-bar-border-radius: 1px;
|
||||
//$progress-bar-sm-border-radius: 1px;
|
||||
//$progress-bar-xs-border-radius: 1px;
|
||||
//
|
||||
// FORMS
|
||||
// --------------------------------------------------------
|
||||
//$input-radius: 0;
|
||||
//
|
||||
// BUTTONS
|
||||
// --------------------------------------------------------
|
||||
//
|
||||
// Border radius for non flat buttons
|
||||
//$btn-border-radius: 3px;
|
||||
//
|
||||
// DIRECT CHAT
|
||||
// --------------------------------------------------------
|
||||
//$direct-chat-height: 250px;
|
||||
//$direct-chat-default-msg-bg: $gray-lte;
|
||||
//$direct-chat-default-font-color: #444;
|
||||
//$direct-chat-default-msg-border-color: $gray-lte;
|
||||
//
|
||||
// CHAT WIDGET
|
||||
// --------------------------------------------------------
|
||||
//$attachment-border-radius: 3px;
|
||||
//
|
||||
// TRANSITIONS SETTINGS
|
||||
// --------------------------------------------------------
|
||||
//
|
||||
// Transition global options
|
||||
//$transition-speed: .3s;
|
||||
//$transition-fn: ease-in-out;
|
||||
//
|
||||
@@ -1,6 +1,6 @@
|
||||
App\Entity\Activity:
|
||||
exclusion_policy: All
|
||||
custom_accessor_order: [id, name, comment, visible, project, fixedRate, hourlyRate]
|
||||
custom_accessor_order: [id, name, comment, visible, project, fixedRate, hourlyRate, color]
|
||||
properties:
|
||||
id:
|
||||
include: true
|
||||
@@ -24,6 +24,9 @@ App\Entity\Activity:
|
||||
include: false
|
||||
exclude: true
|
||||
groups: [Default]
|
||||
color:
|
||||
include: true
|
||||
groups: [Entity]
|
||||
virtual_properties:
|
||||
getProject:
|
||||
serialized_name: project
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
App\Entity\Customer:
|
||||
exclusion_policy: All
|
||||
custom_accessor_order: [id, name, number, comment, visible, company, contact, address, country, currency, phone, fax, mobile, email, homepage, timezone, fixedRate, hourlyRate]
|
||||
custom_accessor_order: [id, name, number, comment, visible, company, contact, address, country, currency, phone, fax, mobile, email, homepage, timezone, fixedRate, hourlyRate, color]
|
||||
properties:
|
||||
id:
|
||||
include: true
|
||||
@@ -56,3 +56,6 @@ App\Entity\Customer:
|
||||
hourlyRate:
|
||||
include: true
|
||||
groups: [Default]
|
||||
color:
|
||||
include: true
|
||||
groups: [Entity]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
App\Entity\Project:
|
||||
exclusion_policy: All
|
||||
custom_accessor_order: [id, name, comment, visible, budget, orderNumber, customer, fixedRate, hourlyRate]
|
||||
custom_accessor_order: [id, name, comment, visible, budget, orderNumber, customer, fixedRate, hourlyRate, color]
|
||||
properties:
|
||||
id:
|
||||
include: true
|
||||
@@ -30,6 +30,9 @@ App\Entity\Project:
|
||||
include: false
|
||||
exclude: true
|
||||
groups: [Default]
|
||||
color:
|
||||
include: true
|
||||
groups: [Entity]
|
||||
virtual_properties:
|
||||
getCustomer:
|
||||
serialized_name: customer
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"build/app.js": "./app.js?[contenthash]",
|
||||
"build/app.css": "./app.css?03894379d3cc9cde71d1ce7c0fe24573",
|
||||
"build/app.css": "./app.css?361d4520765f893b9b0fb94bc9f761c1",
|
||||
"build/fonts/fa-solid-900.woff2": "./fonts/fa-solid-900.woff2?e8a92a29",
|
||||
"build/images/fa-solid-900.svg": "./images/fa-solid-900.svg?666a82cb",
|
||||
"build/images/glyphicons-halflings-regular.svg": "./images/glyphicons-halflings-regular.svg?89889688",
|
||||
|
||||
@@ -67,11 +67,18 @@ class TimesheetEntity
|
||||
$this->project = $entry->getProject()->getName();
|
||||
$this->activity = $entry->getActivity()->getName();
|
||||
|
||||
if (null === $entry->getEnd()) {
|
||||
// TODO move these colors to the controller
|
||||
$this->borderColor = '367fa9'; //'#f39c12';
|
||||
$this->backgroundColor = '#3c8dbc'; //'#f39c12';
|
||||
} else {
|
||||
$color = $entry->getActivity()->getColor();
|
||||
if (empty($color)) {
|
||||
$color = $entry->getProject()->getColor();
|
||||
if (empty($color)) {
|
||||
$color = $entry->getProject()->getCustomer()->getColor();
|
||||
}
|
||||
}
|
||||
|
||||
$this->borderColor = $color;
|
||||
$this->backgroundColor = $color;
|
||||
|
||||
if (null !== $entry->getEnd()) {
|
||||
$this->end = $entry->getEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ class Activity
|
||||
|
||||
// keep the trait include exactly here, for placing the column at the correct position
|
||||
use RatesTrait;
|
||||
use ColorTrait;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
|
||||
41
src/Entity/ColorTrait.php
Normal file
41
src/Entity/ColorTrait.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
trait ColorTrait
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="color", type="string", length=7, nullable=true)
|
||||
*/
|
||||
private $color = null;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getColor(): ?string
|
||||
{
|
||||
return $this->color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $color
|
||||
* @return self
|
||||
*/
|
||||
public function setColor(?string $color = null)
|
||||
{
|
||||
$this->color = $color;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -149,6 +149,7 @@ class Customer
|
||||
|
||||
// keep the trait include exactly here, for placing the column at the correct position
|
||||
use RatesTrait;
|
||||
use ColorTrait;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
|
||||
@@ -85,6 +85,7 @@ class Project
|
||||
|
||||
// keep the trait include exactly here, for placing the column at the correct position
|
||||
use RatesTrait;
|
||||
use ColorTrait;
|
||||
|
||||
/**
|
||||
* @var Timesheet[]
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\Activity;
|
||||
use App\Form\Type\ColorPickerType;
|
||||
use App\Form\Type\CustomerType;
|
||||
use App\Form\Type\FixedRateType;
|
||||
use App\Form\Type\HourlyRateType;
|
||||
@@ -109,6 +110,7 @@ class ActivityEditForm extends AbstractType
|
||||
);
|
||||
|
||||
$builder
|
||||
->add('color', ColorPickerType::class)
|
||||
->add('fixedRate', FixedRateType::class, [
|
||||
'currency' => $currency,
|
||||
])
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\Customer;
|
||||
use App\Form\Type\ColorPickerType;
|
||||
use App\Form\Type\FixedRateType;
|
||||
use App\Form\Type\HourlyRateType;
|
||||
use App\Form\Type\YesNoType;
|
||||
@@ -101,6 +102,7 @@ class CustomerEditForm extends AbstractType
|
||||
->add('timezone', TimezoneType::class, [
|
||||
'label' => 'label.timezone',
|
||||
])
|
||||
->add('color', ColorPickerType::class)
|
||||
->add('fixedRate', FixedRateType::class, [
|
||||
'currency' => $currency ?? false,
|
||||
])
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace App\Form;
|
||||
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
use App\Form\Type\ColorPickerType;
|
||||
use App\Form\Type\CustomerType;
|
||||
use App\Form\Type\FixedRateType;
|
||||
use App\Form\Type\HourlyRateType;
|
||||
@@ -69,6 +70,7 @@ class ProjectEditForm extends AbstractType
|
||||
return $repo->builderForEntityType($customer);
|
||||
},
|
||||
])
|
||||
->add('color', ColorPickerType::class)
|
||||
->add('fixedRate', FixedRateType::class, [
|
||||
'currency' => $currency,
|
||||
])
|
||||
|
||||
67
src/Form/Type/ColorPickerType.php
Normal file
67
src/Form/Type/ColorPickerType.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ColorType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class ColorPickerType extends AbstractType implements DataTransformerInterface
|
||||
{
|
||||
public const DEFAULT_COLOR = '#d2d6de';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->addViewTransformer($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'label' => 'label.color',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($data)
|
||||
{
|
||||
if (empty($data)) {
|
||||
return self::DEFAULT_COLOR;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reverseTransform($data)
|
||||
{
|
||||
return null === $data ? self::DEFAULT_COLOR : $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return ColorType::class;
|
||||
}
|
||||
}
|
||||
52
src/Migrations/Version20190502161758.php
Normal file
52
src/Migrations/Version20190502161758.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use App\Doctrine\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
|
||||
/**
|
||||
* Creates the color columns on: customer, project, activity.
|
||||
*
|
||||
* @version 0.9
|
||||
*/
|
||||
final class Version20190502161758 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Creates the color columns on: customer, project, activity';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$customers = $schema->getTable('kimai2_customers');
|
||||
$customers->addColumn('color', 'string', ['length' => 7, 'notnull' => false, 'default' => null]);
|
||||
|
||||
$projects = $schema->getTable('kimai2_projects');
|
||||
$projects->addColumn('color', 'string', ['length' => 7, 'notnull' => false, 'default' => null]);
|
||||
|
||||
$activities = $schema->getTable('kimai2_activities');
|
||||
$activities->addColumn('color', 'string', ['length' => 7, 'notnull' => false, 'default' => null]);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$customers = $schema->getTable('kimai2_customers');
|
||||
$customers->dropColumn('color');
|
||||
|
||||
$projects = $schema->getTable('kimai2_projects');
|
||||
$projects->dropColumn('color');
|
||||
|
||||
$activities = $schema->getTable('kimai2_activities');
|
||||
$activities->dropColumn('color');
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ class BaseQuery
|
||||
public const ORDER_ASC = 'ASC';
|
||||
public const ORDER_DESC = 'DESC';
|
||||
|
||||
public const DEFAULT_PAGESIZE = 100;
|
||||
public const DEFAULT_PAGESIZE = 50;
|
||||
public const DEFAULT_PAGE = 1;
|
||||
|
||||
public const RESULT_TYPE_OBJECTS = 'Objects';
|
||||
|
||||
@@ -34,19 +34,17 @@
|
||||
|
||||
{% for entry in entries %}
|
||||
<tr{% if is_granted('edit', entry) %} class="modal-ajax-form open-edit" data-href="{{ path('admin_activity_edit', {'id': entry.id}) }}"{% endif %}>
|
||||
<td>{{ entry.name }}</td>
|
||||
<td>{{ widgets.label_color_dot('activity', true, entry.name, null, entry.color) }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'customer') }}">
|
||||
{% if entry.project and entry.project.customer %}
|
||||
<a href="{{ path('admin_customer_edit', {'id' : entry.project.customer.id}) }}">{{ widgets.label_customer(entry.project.customer) }}</a>
|
||||
{% else %}
|
||||
{# GLOBAL ACTIVTITY DOES NOT HAVE A CUSTOMER #}
|
||||
{# only none-global activities have a project and customer assigned #}
|
||||
{{ widgets.label_customer(entry.project.customer, path('admin_customer_edit', {'id' : entry.project.customer.id})) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'project') }}">
|
||||
{% if entry.project %}
|
||||
<a href="{{ path('admin_project_edit', {'id' : entry.project.id}) }}">{{ widgets.label_project(entry.project) }}</a>
|
||||
{% else %}
|
||||
{# GLOBAL ACTIVTITY DOES NOT HAVE A PROJECT #}
|
||||
{# only none-global activities have a project and customer assigned #}
|
||||
{{ widgets.label_project(entry.project, path('admin_project_edit', {'id' : entry.project.id})) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment }}</td>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
{% for entry in entries %}
|
||||
<tr{% if is_granted('edit', entry) %} class="modal-ajax-form open-edit" data-href="{{ path('admin_customer_edit', {'id': entry.id}) }}"{% endif %}>
|
||||
<td>{{ entry.name }} {% if entry.company is not empty %}({{ entry.company }}){% endif %}</td>
|
||||
<td>{{ widgets.label_color_dot('customer', true, entry.name, null, entry.color) }} {% if entry.company is not empty %}({{ entry.company }}){% endif %}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'country') }}">{{ entry.country|country }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'number') }}">{{ entry.number }}</td>
|
||||
|
||||
@@ -42,35 +42,48 @@
|
||||
{{ macro.label(macro.username(user), 'primary') }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro label_activity(activity) %}
|
||||
{% macro label_activity(activity, url) %}
|
||||
{% import _self as macro %}
|
||||
{% set isVisible = activity.visible %}
|
||||
{% set color = activity.color %}
|
||||
{% if color is empty and activity.project is not null %}
|
||||
{% set color = activity.project.color ?? activity.project.customer.color %}
|
||||
{% endif %}
|
||||
{% if isVisible and not activity.project is null %}
|
||||
{% set isVisible = activity.project.visible %}
|
||||
{% if isVisible and not activity.project.customer is null %}
|
||||
{% set isVisible = activity.project.customer.visible %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% set type = (isVisible ? 'primary' : 'default') %}
|
||||
<span class="label label-{{ type }}">{{ activity.name }}</span>
|
||||
{{ macro.label_color_dot('activity', isVisible, activity.name, url, color) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro label_project(project) %}
|
||||
{% macro label_project(project, url) %}
|
||||
{% import _self as macro %}
|
||||
{% set type = 'default' %}
|
||||
{% set isVisible = false %}
|
||||
{% if project.visible and project.customer.visible %}
|
||||
{% set type = 'primary' %}
|
||||
{% set isVisible = true %}
|
||||
{% endif %}
|
||||
<span class="label label-{{ type }}">{{ project.name }}</span>
|
||||
{{ macro.label_color_dot('project', isVisible, project.name, url, (project.color ?? project.customer.color)) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro label_customer(customer) %}
|
||||
{% macro label_customer(customer, url) %}
|
||||
{% import _self as macro %}
|
||||
{% set type = 'default' %}
|
||||
{% if customer.visible %}
|
||||
{% set type = 'primary' %}
|
||||
{% endif %}
|
||||
<span class="label label-{{ type }}">{{ customer.name }}</span>
|
||||
{% set isVisible = customer.visible %}
|
||||
{{ macro.label_color_dot('customer', isVisible, customer.name, url, customer.color) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro label_color_dot(type, isVisible, name, url, color) %}
|
||||
<span class="label-{{ type }}{{ isVisible ? '' : ' label-invisible' }}">
|
||||
<span class="dot"{% if color is not empty %} style="background-color:{{ color }}"{% endif %}></span>
|
||||
<span class="name">
|
||||
{% if url is not empty %}
|
||||
<a href="{{ url }}">{{ name }}</a>
|
||||
{% else %}
|
||||
{{ name }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</span>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro badge_counter(count, url) %}
|
||||
|
||||
@@ -33,9 +33,9 @@
|
||||
|
||||
{% for entry in entries %}
|
||||
<tr{% if is_granted('edit', entry) %} class="modal-ajax-form open-edit" data-href="{{ path('admin_project_edit', {'id': entry.id}) }}"{% endif %}>
|
||||
<td>{{ entry.name }}</td>
|
||||
<td>{{ widgets.label_color_dot('project', true, entry.name, null, entry.color) }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'customer') }}">
|
||||
<a href="{{ path('admin_customer_edit', {'id' : entry.customer.id}) }}">{{ widgets.label_customer(entry.customer) }}</a>
|
||||
{{ widgets.label_customer(entry.customer, path('admin_customer_edit', {'id' : entry.customer.id})) }}
|
||||
</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'budget') }}">{{ entry.budget|money(entry.customer.currency) }}</td>
|
||||
|
||||
@@ -224,7 +224,7 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
$expectedKeys = ['id', 'name', 'visible', 'project', 'hourlyRate', 'fixedRate'];
|
||||
|
||||
if ($full) {
|
||||
$expectedKeys = array_merge($expectedKeys, ['comment']);
|
||||
$expectedKeys = array_merge($expectedKeys, ['comment', 'color']);
|
||||
}
|
||||
|
||||
$actual = array_keys($result);
|
||||
|
||||
@@ -168,7 +168,7 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
|
||||
if ($full) {
|
||||
$expectedKeys = array_merge($expectedKeys, [
|
||||
'homepage', 'number', 'comment', 'company', 'contact', 'address', 'country', 'currency', 'phone', 'fax', 'mobile', 'email', 'timezone'
|
||||
'homepage', 'number', 'comment', 'company', 'contact', 'address', 'country', 'currency', 'phone', 'fax', 'mobile', 'email', 'timezone', 'color'
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
if ($full) {
|
||||
$expectedKeys = array_merge(
|
||||
$expectedKeys,
|
||||
['comment', 'budget', 'orderNumber']
|
||||
['comment', 'budget', 'orderNumber', 'color']
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,11 +66,11 @@ class TimesheetEntityTest extends TestCase
|
||||
$sut->setActivity('cccccccc');
|
||||
$this->assertEquals('cccccccc', $sut->getActivity());
|
||||
|
||||
$this->assertEquals('367fa9', $sut->getBorderColor());
|
||||
$this->assertNull($sut->getBorderColor());
|
||||
$sut->setBorderColor('#cccccc');
|
||||
$this->assertEquals('#cccccc', $sut->getBorderColor());
|
||||
|
||||
$this->assertEquals('#3c8dbc', $sut->getBackgroundColor());
|
||||
$this->assertNull($sut->getBackgroundColor());
|
||||
$sut->setBackgroundColor('#ffffff');
|
||||
$this->assertEquals('#ffffff', $sut->getBackgroundColor());
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ class ActivityTest extends AbstractEntityTest
|
||||
// timesheets
|
||||
$this->assertNull($sut->getFixedRate());
|
||||
$this->assertNull($sut->getHourlyRate());
|
||||
$this->assertNull($sut->getColor());
|
||||
}
|
||||
|
||||
public function testSetterAndGetter()
|
||||
@@ -42,6 +43,9 @@ class ActivityTest extends AbstractEntityTest
|
||||
$this->assertInstanceOf(Activity::class, $sut->setComment('hello world'));
|
||||
$this->assertEquals('hello world', $sut->getComment());
|
||||
|
||||
$this->assertInstanceOf(Activity::class, $sut->setColor('#fffccc'));
|
||||
$this->assertEquals('#fffccc', $sut->getColor());
|
||||
|
||||
$this->assertInstanceOf(Activity::class, $sut->setFixedRate(13.47));
|
||||
$this->assertEquals(13.47, $sut->getFixedRate());
|
||||
$this->assertInstanceOf(Activity::class, $sut->setHourlyRate(99));
|
||||
|
||||
@@ -42,6 +42,7 @@ class CustomerTest extends AbstractEntityTest
|
||||
|
||||
$this->assertNull($sut->getFixedRate());
|
||||
$this->assertNull($sut->getHourlyRate());
|
||||
$this->assertNull($sut->getColor());
|
||||
}
|
||||
|
||||
public function testSetterAndGetter()
|
||||
@@ -57,6 +58,9 @@ class CustomerTest extends AbstractEntityTest
|
||||
$this->assertInstanceOf(Customer::class, $sut->setComment('hello world'));
|
||||
$this->assertEquals('hello world', $sut->getComment());
|
||||
|
||||
$this->assertInstanceOf(Customer::class, $sut->setColor('#fffccc'));
|
||||
$this->assertEquals('#fffccc', $sut->getColor());
|
||||
|
||||
$projects = [(new Project())->setName('Test')];
|
||||
$this->assertInstanceOf(Customer::class, $sut->setProjects($projects));
|
||||
$this->assertSame($projects, $sut->getProjects());
|
||||
|
||||
@@ -33,6 +33,7 @@ class ProjectTest extends AbstractEntityTest
|
||||
$this->assertNull($sut->getFixedRate());
|
||||
$this->assertNull($sut->getHourlyRate());
|
||||
$this->assertNull($sut->getTimesheets());
|
||||
$this->assertNull($sut->getColor());
|
||||
}
|
||||
|
||||
public function testSetterAndGetter()
|
||||
@@ -52,6 +53,9 @@ class ProjectTest extends AbstractEntityTest
|
||||
$this->assertInstanceOf(Project::class, $sut->setComment('a comment'));
|
||||
$this->assertEquals('a comment', $sut->getComment());
|
||||
|
||||
$this->assertInstanceOf(Project::class, $sut->setColor('#fffccc'));
|
||||
$this->assertEquals('#fffccc', $sut->getColor());
|
||||
|
||||
$this->assertInstanceOf(Project::class, $sut->setVisible(false));
|
||||
$this->assertFalse($sut->getVisible());
|
||||
|
||||
|
||||
@@ -337,6 +337,10 @@
|
||||
<source>label.fixedRate</source>
|
||||
<target>Festbetrag</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.color">
|
||||
<source>label.color</source>
|
||||
<target>Farbe</target>
|
||||
</trans-unit>
|
||||
|
||||
<!--
|
||||
User profile
|
||||
|
||||
@@ -337,6 +337,10 @@
|
||||
<source>label.fixedRate</source>
|
||||
<target>Fixed rate</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.color">
|
||||
<source>label.color</source>
|
||||
<target>Color</target>
|
||||
</trans-unit>
|
||||
|
||||
<!--
|
||||
User profile
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user