colors for visual grouping of customer, projects and activities (#751)

This commit is contained in:
Kevin Papst
2019-05-04 01:45:52 +02:00
committed by GitHub
parent e92c2d168e
commit 65df1ff909
40 changed files with 523 additions and 124 deletions

View File

@@ -6,34 +6,16 @@
*/ */
@import "~bootstrap-sass/assets/stylesheets/bootstrap/variables"; @import "~bootstrap-sass/assets/stylesheets/bootstrap/variables";
@import 'variables';
.error-page { @import 'error-page';
margin-bottom: 50px; @import 'print';
}
@media print{
.sf-toolbar, .control-sidebar {display: none !important;}
}
@import 'content'; @import 'content';
@import 'toolbar'; @import 'toolbar';
@import 'sidebar';
/* ================================ SIDEBAR ================================ */ @import 'footer';
/* 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 'invoice'; @import 'invoice';
@import 'datatable'; @import 'datatable';
@import 'datatable-extensions';
@import 'calendar'; @import 'calendar';
@import 'dashboard'; @import 'dashboard';
@import 'navbar'; @import 'navbar';

View 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;
}
}
}

View File

@@ -34,7 +34,7 @@ section.content .overlay,
.overlay-wrapper .overlay { .overlay-wrapper .overlay {
z-index: 50; z-index: 50;
background: rgba(255, 255, 255, 0.7); background: rgba(255, 255, 255, 0.7);
border-radius: 3px; border-radius: $box-border-radius;
} }
.box .overlay.dark, .box .overlay.dark,
section.content .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 { .table-striped.dataTable tbody tr.active:nth-child(odd) th {
background-color: #017ebc; 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;
}
}
}

View 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
View 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%;
}

View File

@@ -76,16 +76,3 @@ li.open .ticktac i.running{
padding-top: 0px; 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
View 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
View 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;
}
}
}

View File

@@ -12,19 +12,6 @@
background: transparent; 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 { .toolbar-pad {
padding: 10px; padding: 10px;
} }

126
assets/sass/variables.scss Normal file
View 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;
//

View File

@@ -1,6 +1,6 @@
App\Entity\Activity: App\Entity\Activity:
exclusion_policy: All 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: properties:
id: id:
include: true include: true
@@ -24,6 +24,9 @@ App\Entity\Activity:
include: false include: false
exclude: true exclude: true
groups: [Default] groups: [Default]
color:
include: true
groups: [Entity]
virtual_properties: virtual_properties:
getProject: getProject:
serialized_name: project serialized_name: project

View File

@@ -1,6 +1,6 @@
App\Entity\Customer: App\Entity\Customer:
exclusion_policy: All 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: properties:
id: id:
include: true include: true
@@ -56,3 +56,6 @@ App\Entity\Customer:
hourlyRate: hourlyRate:
include: true include: true
groups: [Default] groups: [Default]
color:
include: true
groups: [Entity]

View File

@@ -1,6 +1,6 @@
App\Entity\Project: App\Entity\Project:
exclusion_policy: All 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: properties:
id: id:
include: true include: true
@@ -30,6 +30,9 @@ App\Entity\Project:
include: false include: false
exclude: true exclude: true
groups: [Default] groups: [Default]
color:
include: true
groups: [Entity]
virtual_properties: virtual_properties:
getCustomer: getCustomer:
serialized_name: customer serialized_name: customer

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
{ {
"build/app.js": "./app.js?[contenthash]", "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/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/fa-solid-900.svg": "./images/fa-solid-900.svg?666a82cb",
"build/images/glyphicons-halflings-regular.svg": "./images/glyphicons-halflings-regular.svg?89889688", "build/images/glyphicons-halflings-regular.svg": "./images/glyphicons-halflings-regular.svg?89889688",

View File

@@ -67,11 +67,18 @@ class TimesheetEntity
$this->project = $entry->getProject()->getName(); $this->project = $entry->getProject()->getName();
$this->activity = $entry->getActivity()->getName(); $this->activity = $entry->getActivity()->getName();
if (null === $entry->getEnd()) { $color = $entry->getActivity()->getColor();
// TODO move these colors to the controller if (empty($color)) {
$this->borderColor = '367fa9'; //'#f39c12'; $color = $entry->getProject()->getColor();
$this->backgroundColor = '#3c8dbc'; //'#f39c12'; if (empty($color)) {
} else { $color = $entry->getProject()->getCustomer()->getColor();
}
}
$this->borderColor = $color;
$this->backgroundColor = $color;
if (null !== $entry->getEnd()) {
$this->end = $entry->getEnd(); $this->end = $entry->getEnd();
} }
} }

View File

@@ -68,6 +68,7 @@ class Activity
// keep the trait include exactly here, for placing the column at the correct position // keep the trait include exactly here, for placing the column at the correct position
use RatesTrait; use RatesTrait;
use ColorTrait;
/** /**
* @return int * @return int

41
src/Entity/ColorTrait.php Normal file
View 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;
}
}

View File

@@ -149,6 +149,7 @@ class Customer
// keep the trait include exactly here, for placing the column at the correct position // keep the trait include exactly here, for placing the column at the correct position
use RatesTrait; use RatesTrait;
use ColorTrait;
/** /**
* @return int * @return int

View File

@@ -85,6 +85,7 @@ class Project
// keep the trait include exactly here, for placing the column at the correct position // keep the trait include exactly here, for placing the column at the correct position
use RatesTrait; use RatesTrait;
use ColorTrait;
/** /**
* @var Timesheet[] * @var Timesheet[]

View File

@@ -10,6 +10,7 @@
namespace App\Form; namespace App\Form;
use App\Entity\Activity; use App\Entity\Activity;
use App\Form\Type\ColorPickerType;
use App\Form\Type\CustomerType; use App\Form\Type\CustomerType;
use App\Form\Type\FixedRateType; use App\Form\Type\FixedRateType;
use App\Form\Type\HourlyRateType; use App\Form\Type\HourlyRateType;
@@ -109,6 +110,7 @@ class ActivityEditForm extends AbstractType
); );
$builder $builder
->add('color', ColorPickerType::class)
->add('fixedRate', FixedRateType::class, [ ->add('fixedRate', FixedRateType::class, [
'currency' => $currency, 'currency' => $currency,
]) ])

View File

@@ -10,6 +10,7 @@
namespace App\Form; namespace App\Form;
use App\Entity\Customer; use App\Entity\Customer;
use App\Form\Type\ColorPickerType;
use App\Form\Type\FixedRateType; use App\Form\Type\FixedRateType;
use App\Form\Type\HourlyRateType; use App\Form\Type\HourlyRateType;
use App\Form\Type\YesNoType; use App\Form\Type\YesNoType;
@@ -101,6 +102,7 @@ class CustomerEditForm extends AbstractType
->add('timezone', TimezoneType::class, [ ->add('timezone', TimezoneType::class, [
'label' => 'label.timezone', 'label' => 'label.timezone',
]) ])
->add('color', ColorPickerType::class)
->add('fixedRate', FixedRateType::class, [ ->add('fixedRate', FixedRateType::class, [
'currency' => $currency ?? false, 'currency' => $currency ?? false,
]) ])

View File

@@ -11,6 +11,7 @@ namespace App\Form;
use App\Entity\Customer; use App\Entity\Customer;
use App\Entity\Project; use App\Entity\Project;
use App\Form\Type\ColorPickerType;
use App\Form\Type\CustomerType; use App\Form\Type\CustomerType;
use App\Form\Type\FixedRateType; use App\Form\Type\FixedRateType;
use App\Form\Type\HourlyRateType; use App\Form\Type\HourlyRateType;
@@ -69,6 +70,7 @@ class ProjectEditForm extends AbstractType
return $repo->builderForEntityType($customer); return $repo->builderForEntityType($customer);
}, },
]) ])
->add('color', ColorPickerType::class)
->add('fixedRate', FixedRateType::class, [ ->add('fixedRate', FixedRateType::class, [
'currency' => $currency, 'currency' => $currency,
]) ])

View 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;
}
}

View 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');
}
}

View File

@@ -17,7 +17,7 @@ class BaseQuery
public const ORDER_ASC = 'ASC'; public const ORDER_ASC = 'ASC';
public const ORDER_DESC = 'DESC'; public const ORDER_DESC = 'DESC';
public const DEFAULT_PAGESIZE = 100; public const DEFAULT_PAGESIZE = 50;
public const DEFAULT_PAGE = 1; public const DEFAULT_PAGE = 1;
public const RESULT_TYPE_OBJECTS = 'Objects'; public const RESULT_TYPE_OBJECTS = 'Objects';

View File

@@ -34,19 +34,17 @@
{% for entry in entries %} {% 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 %}> <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') }}"> <td class="{{ tables.data_table_column_class(tableName, columns, 'customer') }}">
{% if entry.project and entry.project.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> {# only none-global activities have a project and customer assigned #}
{% else %} {{ widgets.label_customer(entry.project.customer, path('admin_customer_edit', {'id' : entry.project.customer.id})) }}
{# GLOBAL ACTIVTITY DOES NOT HAVE A CUSTOMER #}
{% endif %} {% endif %}
</td> </td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'project') }}"> <td class="{{ tables.data_table_column_class(tableName, columns, 'project') }}">
{% if entry.project %} {% if entry.project %}
<a href="{{ path('admin_project_edit', {'id' : entry.project.id}) }}">{{ widgets.label_project(entry.project) }}</a> {# only none-global activities have a project and customer assigned #}
{% else %} {{ widgets.label_project(entry.project, path('admin_project_edit', {'id' : entry.project.id})) }}
{# GLOBAL ACTIVTITY DOES NOT HAVE A PROJECT #}
{% endif %} {% endif %}
</td> </td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment }}</td> <td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment }}</td>

View File

@@ -34,7 +34,7 @@
{% for entry in entries %} {% 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 %}> <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, '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, 'country') }}">{{ entry.country|country }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'number') }}">{{ entry.number }}</td> <td class="{{ tables.data_table_column_class(tableName, columns, 'number') }}">{{ entry.number }}</td>

View File

@@ -42,35 +42,48 @@
{{ macro.label(macro.username(user), 'primary') }} {{ macro.label(macro.username(user), 'primary') }}
{% endmacro %} {% endmacro %}
{% macro label_activity(activity) %} {% macro label_activity(activity, url) %}
{% import _self as macro %} {% import _self as macro %}
{% set isVisible = activity.visible %} {% 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 %} {% if isVisible and not activity.project is null %}
{% set isVisible = activity.project.visible %} {% set isVisible = activity.project.visible %}
{% if isVisible and not activity.project.customer is null %} {% if isVisible and not activity.project.customer is null %}
{% set isVisible = activity.project.customer.visible %} {% set isVisible = activity.project.customer.visible %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% set type = (isVisible ? 'primary' : 'default') %} {{ macro.label_color_dot('activity', isVisible, activity.name, url, color) }}
<span class="label label-{{ type }}">{{ activity.name }}</span>
{% endmacro %} {% endmacro %}
{% macro label_project(project) %} {% macro label_project(project, url) %}
{% import _self as macro %} {% import _self as macro %}
{% set type = 'default' %} {% set isVisible = false %}
{% if project.visible and project.customer.visible %} {% if project.visible and project.customer.visible %}
{% set type = 'primary' %} {% set isVisible = true %}
{% endif %} {% endif %}
<span class="label label-{{ type }}">{{ project.name }}</span> {{ macro.label_color_dot('project', isVisible, project.name, url, (project.color ?? project.customer.color)) }}
{% endmacro %} {% endmacro %}
{% macro label_customer(customer) %} {% macro label_customer(customer, url) %}
{% import _self as macro %} {% import _self as macro %}
{% set type = 'default' %} {% set isVisible = customer.visible %}
{% if customer.visible %} {{ macro.label_color_dot('customer', isVisible, customer.name, url, customer.color) }}
{% set type = 'primary' %} {% endmacro %}
{% endif %}
<span class="label label-{{ type }}">{{ customer.name }}</span> {% 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 %} {% endmacro %}
{% macro badge_counter(count, url) %} {% macro badge_counter(count, url) %}

View File

@@ -33,9 +33,9 @@
{% for entry in entries %} {% 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 %}> <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') }}"> <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>
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment }}</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> <td class="{{ tables.data_table_column_class(tableName, columns, 'budget') }}">{{ entry.budget|money(entry.customer.currency) }}</td>

View File

@@ -224,7 +224,7 @@ class ActivityControllerTest extends APIControllerBaseTest
$expectedKeys = ['id', 'name', 'visible', 'project', 'hourlyRate', 'fixedRate']; $expectedKeys = ['id', 'name', 'visible', 'project', 'hourlyRate', 'fixedRate'];
if ($full) { if ($full) {
$expectedKeys = array_merge($expectedKeys, ['comment']); $expectedKeys = array_merge($expectedKeys, ['comment', 'color']);
} }
$actual = array_keys($result); $actual = array_keys($result);

View File

@@ -168,7 +168,7 @@ class CustomerControllerTest extends APIControllerBaseTest
if ($full) { if ($full) {
$expectedKeys = array_merge($expectedKeys, [ $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'
]); ]);
} }

View File

@@ -220,7 +220,7 @@ class ProjectControllerTest extends APIControllerBaseTest
if ($full) { if ($full) {
$expectedKeys = array_merge( $expectedKeys = array_merge(
$expectedKeys, $expectedKeys,
['comment', 'budget', 'orderNumber'] ['comment', 'budget', 'orderNumber', 'color']
); );
} }

View File

@@ -66,11 +66,11 @@ class TimesheetEntityTest extends TestCase
$sut->setActivity('cccccccc'); $sut->setActivity('cccccccc');
$this->assertEquals('cccccccc', $sut->getActivity()); $this->assertEquals('cccccccc', $sut->getActivity());
$this->assertEquals('367fa9', $sut->getBorderColor()); $this->assertNull($sut->getBorderColor());
$sut->setBorderColor('#cccccc'); $sut->setBorderColor('#cccccc');
$this->assertEquals('#cccccc', $sut->getBorderColor()); $this->assertEquals('#cccccc', $sut->getBorderColor());
$this->assertEquals('#3c8dbc', $sut->getBackgroundColor()); $this->assertNull($sut->getBackgroundColor());
$sut->setBackgroundColor('#ffffff'); $sut->setBackgroundColor('#ffffff');
$this->assertEquals('#ffffff', $sut->getBackgroundColor()); $this->assertEquals('#ffffff', $sut->getBackgroundColor());

View File

@@ -27,6 +27,7 @@ class ActivityTest extends AbstractEntityTest
// timesheets // timesheets
$this->assertNull($sut->getFixedRate()); $this->assertNull($sut->getFixedRate());
$this->assertNull($sut->getHourlyRate()); $this->assertNull($sut->getHourlyRate());
$this->assertNull($sut->getColor());
} }
public function testSetterAndGetter() public function testSetterAndGetter()
@@ -42,6 +43,9 @@ class ActivityTest extends AbstractEntityTest
$this->assertInstanceOf(Activity::class, $sut->setComment('hello world')); $this->assertInstanceOf(Activity::class, $sut->setComment('hello world'));
$this->assertEquals('hello world', $sut->getComment()); $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->assertInstanceOf(Activity::class, $sut->setFixedRate(13.47));
$this->assertEquals(13.47, $sut->getFixedRate()); $this->assertEquals(13.47, $sut->getFixedRate());
$this->assertInstanceOf(Activity::class, $sut->setHourlyRate(99)); $this->assertInstanceOf(Activity::class, $sut->setHourlyRate(99));

View File

@@ -42,6 +42,7 @@ class CustomerTest extends AbstractEntityTest
$this->assertNull($sut->getFixedRate()); $this->assertNull($sut->getFixedRate());
$this->assertNull($sut->getHourlyRate()); $this->assertNull($sut->getHourlyRate());
$this->assertNull($sut->getColor());
} }
public function testSetterAndGetter() public function testSetterAndGetter()
@@ -57,6 +58,9 @@ class CustomerTest extends AbstractEntityTest
$this->assertInstanceOf(Customer::class, $sut->setComment('hello world')); $this->assertInstanceOf(Customer::class, $sut->setComment('hello world'));
$this->assertEquals('hello world', $sut->getComment()); $this->assertEquals('hello world', $sut->getComment());
$this->assertInstanceOf(Customer::class, $sut->setColor('#fffccc'));
$this->assertEquals('#fffccc', $sut->getColor());
$projects = [(new Project())->setName('Test')]; $projects = [(new Project())->setName('Test')];
$this->assertInstanceOf(Customer::class, $sut->setProjects($projects)); $this->assertInstanceOf(Customer::class, $sut->setProjects($projects));
$this->assertSame($projects, $sut->getProjects()); $this->assertSame($projects, $sut->getProjects());

View File

@@ -33,6 +33,7 @@ class ProjectTest extends AbstractEntityTest
$this->assertNull($sut->getFixedRate()); $this->assertNull($sut->getFixedRate());
$this->assertNull($sut->getHourlyRate()); $this->assertNull($sut->getHourlyRate());
$this->assertNull($sut->getTimesheets()); $this->assertNull($sut->getTimesheets());
$this->assertNull($sut->getColor());
} }
public function testSetterAndGetter() public function testSetterAndGetter()
@@ -52,6 +53,9 @@ class ProjectTest extends AbstractEntityTest
$this->assertInstanceOf(Project::class, $sut->setComment('a comment')); $this->assertInstanceOf(Project::class, $sut->setComment('a comment'));
$this->assertEquals('a comment', $sut->getComment()); $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->assertInstanceOf(Project::class, $sut->setVisible(false));
$this->assertFalse($sut->getVisible()); $this->assertFalse($sut->getVisible());

View File

@@ -337,6 +337,10 @@
<source>label.fixedRate</source> <source>label.fixedRate</source>
<target>Festbetrag</target> <target>Festbetrag</target>
</trans-unit> </trans-unit>
<trans-unit id="label.color">
<source>label.color</source>
<target>Farbe</target>
</trans-unit>
<!-- <!--
User profile User profile

View File

@@ -337,6 +337,10 @@
<source>label.fixedRate</source> <source>label.fixedRate</source>
<target>Fixed rate</target> <target>Fixed rate</target>
</trans-unit> </trans-unit>
<trans-unit id="label.color">
<source>label.color</source>
<target>Color</target>
</trans-unit>
<!-- <!--
User profile User profile

Binary file not shown.