improvements
@@ -61,6 +61,10 @@
|
||||
<source>menu.admin_activity</source>
|
||||
<target>Aktivitäten</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="menu.admin_user">
|
||||
<source>menu.admin_user</source>
|
||||
<target>Benutzer</target>
|
||||
</trans-unit>
|
||||
|
||||
<!--
|
||||
Footer
|
||||
@@ -181,6 +185,18 @@
|
||||
<source>dashboard.subtitle</source>
|
||||
<target>Willkommen!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="dashboard.you">
|
||||
<source>dashboard.you</source>
|
||||
<target>Deine Statistiken</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="dashboard.all">
|
||||
<source>dashboard.all</source>
|
||||
<target>Alle Benutzer</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="dashboard.admin">
|
||||
<source>dashboard.admin</source>
|
||||
<target>Administrator</target>
|
||||
</trans-unit>
|
||||
|
||||
<!--
|
||||
Widgets
|
||||
@@ -210,6 +226,18 @@
|
||||
<target>Verwalten Sie Ihre Zeiteinträge</target>
|
||||
</trans-unit>
|
||||
|
||||
<!--
|
||||
User profile
|
||||
-->
|
||||
<trans-unit id="profile.title">
|
||||
<source>profile.title</source>
|
||||
<target>Benutzerprofil</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="profile.subtitle">
|
||||
<source>profile.subtitle</source>
|
||||
<target>Verwalte Deine Einstellungen</target>
|
||||
</trans-unit>
|
||||
|
||||
<!--
|
||||
Admin: Timesheet
|
||||
-->
|
||||
@@ -245,6 +273,80 @@
|
||||
<source>admin_activity.subtitle</source>
|
||||
<target>Aufgaben zur Zeiterfassung</target>
|
||||
</trans-unit>
|
||||
|
||||
<!--
|
||||
Admin: User
|
||||
-->
|
||||
<trans-unit id="admin_user.title">
|
||||
<source>admin_user.title</source>
|
||||
<target>Benutzer</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="admin_user.subtitle">
|
||||
<source>admin_user.subtitle</source>
|
||||
<target>Alle registrierten Nutzer</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.alias">
|
||||
<source>label.alias</source>
|
||||
<target>Name</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.email">
|
||||
<source>label.email</source>
|
||||
<target>Email</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.title">
|
||||
<source>label.title</source>
|
||||
<target>Titel</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.active">
|
||||
<source>label.active</source>
|
||||
<target>Aktiv</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.roles">
|
||||
<source>label.roles</source>
|
||||
<target>Rolle</target>
|
||||
</trans-unit>
|
||||
|
||||
<!--
|
||||
ROLES
|
||||
-->
|
||||
<trans-unit id="ROLE_ADMIN">
|
||||
<source>ROLE_ADMIN</source>
|
||||
<target>Administrator</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ROLE_TEAMLEAD">
|
||||
<source>ROLE_TEAMLEAD</source>
|
||||
<target>Teamleiter</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ROLE_USER">
|
||||
<source>ROLE_USER</source>
|
||||
<target>Benutzer</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ROLE_CUSTOMER">
|
||||
<source>ROLE_CUSTOMER</source>
|
||||
<target>Kunde</target>
|
||||
</trans-unit>
|
||||
|
||||
<!--
|
||||
Statistics
|
||||
-->
|
||||
<trans-unit id="stats.durationThisMonth">
|
||||
<source>stats.durationThisMonth</source>
|
||||
<target>Arbeitszeit diesen Monat</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="stats.amountThisMonth">
|
||||
<source>stats.amountThisMonth</source>
|
||||
<target>Umsatz diesen Monat</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="stats.durationTotal">
|
||||
<source>stats.durationTotal</source>
|
||||
<target>Arbeitszeit total</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="stats.amountTotal">
|
||||
<source>stats.amountTotal</source>
|
||||
<target>Umsatz total</target>
|
||||
</trans-unit>
|
||||
|
||||
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
37
app/Resources/views/admin/user.html.twig
Normal file
@@ -0,0 +1,37 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% import "macros/datatables.html.twig" as tables %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% import "AvanzuAdminThemeBundle:layout:macros.html.twig" as macro %}
|
||||
|
||||
{% block page_title %}{{ 'admin_user.title'|trans }}{% endblock %}
|
||||
{% block page_subtitle %}{{ 'admin_user.subtitle'|trans }}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% if entries.count > 0 %}
|
||||
{{ tables.data_table_header({
|
||||
'label.id': '',
|
||||
'label.username': 'user',
|
||||
'label.alias': 'comment-o',
|
||||
'label.email': 'envelope-o',
|
||||
'label.title': 'graduation-cap',
|
||||
'label.active': 'lock',
|
||||
'label.roles': 'users',
|
||||
}) }}
|
||||
|
||||
{% for entry in entries %}
|
||||
<tr>
|
||||
<td>{{ entry.id }}</td>
|
||||
<td>{{ entry.username }}</td>
|
||||
<td>{{ entry.alias }}</td>
|
||||
<td>{{ entry.email }}</td>
|
||||
<td>{{ entry.title }}</td>
|
||||
<td>{{ widgets.label_visible(entry.active) }}</td>
|
||||
<td>{{ entry.roles|join(',')|trans }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
{{ tables.data_table_footer(entries, 'admin_user_paginated') }}
|
||||
{% else %}
|
||||
{{ widgets.callout('warning', 'error.no_entries_found') }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -36,6 +36,25 @@
|
||||
{{ include('default/_flash_messages.html.twig') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block avanzu_head %}
|
||||
<link rel="stylesheet" href="{{ asset('css/kimai.css') }}">
|
||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
||||
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
|
||||
<script src="{{ asset('js/kimai.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$(document).kimai({imagePath: '{{ asset('images') }}'});
|
||||
$(document).kimai('ticktac', 'a#ticktac');
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block avanzu_sidebar_user %}
|
||||
{% if app.user is not null and is_granted('IS_AUTHENTICATED_FULLY') %}
|
||||
{{ render(controller('TimesheetBundle:Timesheet:statusEntry')) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block avanzu_footer %}
|
||||
{% block footer %}
|
||||
<footer class="main-footer">
|
||||
@@ -49,8 +68,6 @@
|
||||
{% endblock %}
|
||||
|
||||
{#
|
||||
|
||||
avanzu_head
|
||||
avanzu_navbar_toggle
|
||||
avanzu_navbar_messages
|
||||
avanzu_navbar_notifications
|
||||
|
||||
@@ -6,21 +6,10 @@
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('Bla blub', 300, 'bar-chart', 'blue') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('Steigerungsrate', 80, 'line-chart', 'red') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('Anzahl Tage', 27, 'calendar', 'yellow') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('Stunden total / Monat', 460, 'clock-o', 'green') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# FIXME Übersetzungen hinzufügen #}
|
||||
{# blue / yellow / purple / green / black #}
|
||||
{# bar-chart / line-chart / calendar / clock-o #}
|
||||
{#
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_progress('Bewilligte Stunden', 'Stunden zur Abrechnung bewilligt', 120, 10, 'star-o') }}
|
||||
@@ -35,19 +24,79 @@
|
||||
{{ widgets.info_box_progress('Anzahl Benutzer', 'Mehr ist besser!', 5, 90, 'user') }}
|
||||
</div>
|
||||
</div>
|
||||
#}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_more('Bewilligte Stunden', 120, 'h', 'star-o', 'purple') }}
|
||||
{% if is_granted('ROLE_USER') %}
|
||||
{{ widgets.page_header('dashboard.you') }}
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('stats.durationThisMonth', timesheetUser.durationThisMonth|duration(true), 'hourglass-o', 'blue') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('stats.amountThisMonth', timesheetUser.amountThisMonth|money, 'money', 'green') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('stats.durationTotal', timesheetUser.durationTotal|duration(true), 'hourglass-o', 'yellow') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('stats.amountTotal', timesheetUser.amountTotal|money, 'money', 'red') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_more('Umsatz / Monat', 6830, '$', '', 'yellow') }}
|
||||
{% endif %}
|
||||
|
||||
{% if is_granted('ROLE_TEAMLEAD') %}
|
||||
{{ widgets.page_header('dashboard.all') }}
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('Arbeitszeit diesen Monat', timesheetGlobal.durationThisMonth|duration(true), 'hourglass-o', 'blue') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('Umsatz diesen Monat', timesheetGlobal.amountThisMonth|money, 'money', 'green') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('Arbeitszeit total', timesheetGlobal.durationTotal|duration(true), 'hourglass-o', 'yellow') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('Umsatz total', timesheetGlobal.amountTotal|money, 'money', 'red') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_more('Stunden persönlich', 135, '€', 'hourglass-o', 'red') }}
|
||||
{% endif %}
|
||||
|
||||
{% if is_granted('ROLE_TEAMLEAD') %}
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('Anzahl Benutzer', user.totalAmount, 'users', 'blue') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('Aktive Benutzer diesen Monat', timesheetGlobal.activeThisMonth, 'users', 'yellow') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('Aktive Benutzer jemals', timesheetGlobal.activeTotal, 'users', 'red') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_counter('Momentan aktiv', timesheetGlobal.activeCurrently, 'users', 'green') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_more('Anzahl Benutzer', 5, '', 'user') }}
|
||||
{% endif %}
|
||||
|
||||
{% if is_granted('ROLE_ADMIN') %}
|
||||
{{ widgets.page_header('dashboard.admin') }}
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_more('Anzahl Aktivitäten', activity.totalAmount, '', path('admin_activity'), 'tasks', 'purple') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_more('Anzahl Projekte', project.totalAmount, '', path('admin_project'), 'book', 'yellow') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{{ widgets.info_box_more('Anzahl Benutzer', user.totalAmount, ' ', path('admin_user'), 'users') }}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{# FIXME #}
|
||||
{{ widgets.info_box_more('Momentan aktiv', timesheetGlobal.activeCurrently, '', '#', 'hourglass-o', 'red') }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
53
app/Resources/views/macros/datatables.html.twig
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
{% macro data_table_header(entries) %}
|
||||
<div class="box">
|
||||
{# <div class="box-header">
|
||||
<h3 class="box-title">Hover Data Table</h3>
|
||||
</div> #}
|
||||
<div class="box-body no-padding">
|
||||
<div class="dataTables_wrapper form-inline dt-bootstrap">
|
||||
<div class="row">
|
||||
<div class="col-sm-6"></div>
|
||||
<div class="col-sm-6"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<table class="table table-striped table-hover dataTable" role="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for title, icon in entries %}
|
||||
<th>
|
||||
{% if kimai_context.table_icons and icon %}<i class="fa fa-{{ icon }}"></i>{% endif %}
|
||||
{{ title|trans }}
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro data_table_footer(entries, route) %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{#<div class="row">
|
||||
<div class="col-sm-5">
|
||||
{#<div class="dataTables_info" id="example2_info" role="status" aria-live="polite">
|
||||
{{ 'datatables.entry_counter'|trans({'%from%': '1', '%to%': '10', '%total%': entries.count}) }}
|
||||
</div># }
|
||||
</div>
|
||||
<div class="col-sm-7">
|
||||
<div class="dataTables_paginate paging_simple_numbers">
|
||||
{{ pagerfanta(entries, 'twitter_bootstrap3_translated', { routeName: route }) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>#}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="navigation text-center">
|
||||
{{ pagerfanta(entries, 'twitter_bootstrap3_translated', { routeName: route }) }}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
@@ -1,10 +1,14 @@
|
||||
|
||||
{% macro badge_visible(visible) %}
|
||||
{% macro page_header(title) %}
|
||||
<h2 class="page-header">{{ title|trans }}</h2>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro label_visible(visible) %}
|
||||
{% import _self as macro %}
|
||||
{% if visible %}
|
||||
{{ macro.badge('badge.visible', 'green') }}
|
||||
{{ macro.label('badge.visible', 'success') }}
|
||||
{% else %}
|
||||
{{ macro.badge('badge.invisible', 'red') }}
|
||||
{{ macro.label('badge.invisible', 'warning') }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
@@ -16,7 +20,13 @@
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro label(title, type) %}
|
||||
{# success, warning, danger, primary #}
|
||||
<span class="label label-{{ type|default('success') }}">{{ title|trans }}</span>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro badge(title, color) %}
|
||||
{# black, green, blue, yellow #}
|
||||
<span class="badge bg-{{ color|default('red') }}">{{ title|trans }}</span>
|
||||
{% endmacro %}
|
||||
|
||||
@@ -35,55 +45,6 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro profile_list(user, items, color) %}
|
||||
{% import "AvanzuAdminThemeBundle:layout:macros.html.twig" as macro %}
|
||||
<div class="box box-widget widget-user-2">
|
||||
<!-- Add the bg color to the header using any of the bg-* classes -->
|
||||
<div class="widget-user-header bg-{{ color|default(kimai_context.box_color) }}">
|
||||
<div class="widget-user-image">
|
||||
{{ macro.avatar(user.avatar, user.username) }}
|
||||
</div>
|
||||
<!-- /.widget-user-image -->
|
||||
<h3 class="widget-user-username">{{ user.alias }} ({{ user.username }})</h3>
|
||||
<h5 class="widget-user-desc">{{ user.title }}</h5>
|
||||
</div>
|
||||
<div class="box-footer no-padding">
|
||||
<ul class="nav nav-stacked">
|
||||
{% for entry in items %}
|
||||
<li><a href="{{ entry.url }}">{{ entry.title|trans }} <span class="pull-right badge bg-{{ entry.color }}">{{ entry.amount }}</span></a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro profile_box(user, items) %}
|
||||
{% import "AvanzuAdminThemeBundle:layout:macros.html.twig" as macro %}
|
||||
|
||||
<div class="box box-widget widget-user">
|
||||
<!-- Add the bg color to the header using any of the bg-* classes -->
|
||||
<div class="widget-user-header bg-{{ color|default(kimai_context.box_color) }}">
|
||||
<h3 class="widget-user-username">{{ user.alias }} ({{ user.username }})</h3>
|
||||
<h5 class="widget-user-desc">{{ user.title }}</h5>
|
||||
</div>
|
||||
<div class="widget-user-image">
|
||||
{{ macro.avatar(user.avatar, user.username) }}
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<div class="row">
|
||||
{% for entry in items|slice(0, 3) %}
|
||||
<div class="col-sm-4 border-right">
|
||||
<div class="description-block">
|
||||
<h5 class="description-header">{{ entry.amount }}</h5>
|
||||
<span class="description-text">{{ entry.title|trans }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro info_box_counter(title, amount, icon, color) %}
|
||||
<div class="info-box">
|
||||
<span class="info-box-icon bg-{{ color|default(kimai_context.box_color) }}"><i class="fa fa-{{ icon|default('flag-o') }}"></i></span>
|
||||
@@ -114,16 +75,16 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro info_box_more(title, amount, unit, icon, color) %}
|
||||
{% macro info_box_more(title, amount, unit, url, icon, color) %}
|
||||
<div class="small-box bg-{{ color|default(kimai_context.box_color) }}">
|
||||
<div class="inner">
|
||||
<h3>{{ amount }}<sup style="font-size: 20px">{{ unit|default('%') }}</sup></h3>
|
||||
<h3>{{ amount }}<sup style="font-size: 20px">{{ unit|default('') }}</sup></h3>
|
||||
<p>{{ title|trans }}</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fa fa-{{ icon|default('bar-chart') }}"></i>
|
||||
</div>
|
||||
<a href="#" class="small-box-footer">
|
||||
<a href="{{ url }}" class="small-box-footer">
|
||||
{{ 'more.info.link'|trans }} <i class="fa fa-arrow-circle-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,81 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
|
||||
{% block page_title %}Profile{% endblock %}
|
||||
{% block page_subtitle %}manage your settings{% endblock %}
|
||||
{% block page_title %}{{ 'profile.title'|trans }}{% endblock %}
|
||||
{% block page_subtitle %}{{ 'profile.subtitle'|trans }}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{{ widgets.profile_list(user, items, 'aqua-active') }}
|
||||
{{ widgets.profile_box(user, items, 'aqua-active') }}
|
||||
{% endblock %}
|
||||
{% import _self as widgets %}
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12 col-xs-12">
|
||||
{{ widgets.profile_list(user, settings, 'aqua-active') }}
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12 col-xs-12">
|
||||
{{ widgets.profile_box(user, stats, 'aqua-active') }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% macro profile_list(user, items, color) %}
|
||||
{% import "AvanzuAdminThemeBundle:layout:macros.html.twig" as macro %}
|
||||
<div class="box box-widget widget-user-2">
|
||||
<!-- Add the bg color to the header using any of the bg-* classes -->
|
||||
<div class="widget-user-header bg-{{ color|default(kimai_context.box_color) }}">
|
||||
<div class="widget-user-image">
|
||||
{{ macro.avatar(user.avatar, user.username) }}
|
||||
</div>
|
||||
<!-- /.widget-user-image -->
|
||||
<h3 class="widget-user-username">{{ user.alias }} ({{ user.username }})</h3>
|
||||
<h5 class="widget-user-desc">{{ user.title }}</h5>
|
||||
</div>
|
||||
<div class="box-footer no-padding">
|
||||
<ul class="nav nav-stacked">
|
||||
{% for entry in items %}
|
||||
<li><a href="{{ entry.url }}">{{ entry.title|trans }} <span class="pull-right badge bg-{{ entry.color }}">{{ entry.value }}</span></a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro profile_box(user, stats, color) %}
|
||||
{% import "AvanzuAdminThemeBundle:layout:macros.html.twig" as macro %}
|
||||
|
||||
<div class="box box-widget widget-user">
|
||||
<!-- Add the bg color to the header using any of the bg-* classes -->
|
||||
<div class="widget-user-header bg-{{ color|default(kimai_context.box_color) }}">
|
||||
<h3 class="widget-user-username">{{ user.alias }} ({{ user.username }})</h3>
|
||||
<h5 class="widget-user-desc">{{ user.title }}</h5>
|
||||
</div>
|
||||
<div class="widget-user-image">
|
||||
{{ macro.avatar(user.avatar, user.username) }}
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<div class="row">
|
||||
<div class="col-sm-3 border-right">
|
||||
<div class="description-block">
|
||||
<h5 class="description-header">{{ stats.durationTotal|duration }}</h5>
|
||||
<span class="description-text">{{ 'stats.durationTotal'|trans }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 border-right">
|
||||
<div class="description-block">
|
||||
<h5 class="description-header">{{ stats.amountTotal|money }}</h5>
|
||||
<span class="description-text">{{ 'stats.amountTotal'|trans }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 border-right">
|
||||
<div class="description-block">
|
||||
<h5 class="description-header">{{ stats.durationThisMonth|duration }}</h5>
|
||||
<span class="description-text">{{ 'stats.durationThisMonth'|trans }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 border-right">
|
||||
<div class="description-block">
|
||||
<h5 class="description-header">{{ stats.amountThisMonth|money }}</h5>
|
||||
<span class="description-text">{{ 'stats.amountThisMonth'|trans }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
@@ -61,6 +61,7 @@ twig:
|
||||
kimai_context:
|
||||
date_1: "d.m.Y" # used for display in timesheets
|
||||
box_color: "green"
|
||||
table_icons: false
|
||||
|
||||
# Assetic Configuration (used for managing web assets: CSS, JavaScript, Sass, etc.)
|
||||
assetic:
|
||||
|
||||
75
composer.lock
generated
@@ -4,34 +4,75 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "8c9fa238b0d9190a97312f91782fb7a2",
|
||||
"content-hash": "5c5c4734f4e8e74afdfd434661f75a7d",
|
||||
"hash": "4987a4da3f291b8e0bb1b3af06a5cfa6",
|
||||
"content-hash": "287d1663444888a10331136287c4269b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "avanzu/admin-theme-bundle",
|
||||
"version": "1.3.4",
|
||||
"name": "almasaeed2010/adminlte",
|
||||
"version": "v2.3.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/avanzu/AdminThemeBundle.git",
|
||||
"reference": "24a1ac93ace744b5e8cffe6681009a4cc8f3a133"
|
||||
"url": "https://github.com/almasaeed2010/AdminLTE.git",
|
||||
"reference": "797b65b3ca90525a4e048de5b8d02ba6878ea239"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/avanzu/AdminThemeBundle/zipball/24a1ac93ace744b5e8cffe6681009a4cc8f3a133",
|
||||
"reference": "24a1ac93ace744b5e8cffe6681009a4cc8f3a133",
|
||||
"url": "https://api.github.com/repos/almasaeed2010/AdminLTE/zipball/797b65b3ca90525a4e048de5b8d02ba6878ea239",
|
||||
"reference": "797b65b3ca90525a4e048de5b8d02ba6878ea239",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Abdullah Almsaeed",
|
||||
"email": "abdullah@almsaeedstudio.com"
|
||||
}
|
||||
],
|
||||
"description": "AdminLTE - admin control panel and dashboard that's based on Bootstrap 3",
|
||||
"homepage": "http://almsaeedstudio.com/",
|
||||
"keywords": [
|
||||
"JS",
|
||||
"admin",
|
||||
"back-end",
|
||||
"css",
|
||||
"less",
|
||||
"responsive",
|
||||
"template",
|
||||
"theme",
|
||||
"web"
|
||||
],
|
||||
"time": "2016-10-19 15:34:25"
|
||||
},
|
||||
{
|
||||
"name": "avanzu/admin-theme-bundle",
|
||||
"version": "dev-feature/2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/avanzu/AdminThemeBundle.git",
|
||||
"reference": "b2728cd76434cafd5f37dd0b75fe133429d2b1e0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/avanzu/AdminThemeBundle/zipball/b2728cd76434cafd5f37dd0b75fe133429d2b1e0",
|
||||
"reference": "b2728cd76434cafd5f37dd0b75fe133429d2b1e0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"symfony/assetic-bundle": ">=2.3",
|
||||
"almasaeed2010/adminlte": "^2.3",
|
||||
"php": ">=5.5.9",
|
||||
"symfony/event-dispatcher": ">=2.3",
|
||||
"symfony/http-foundation": ">=2.3",
|
||||
"symfony/http-kernel": ">=2.3",
|
||||
"symfony/twig-bundle": ">=2.3"
|
||||
"symfony/http-kernel": ">=2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"hamcrest/hamcrest-php": "dev-master",
|
||||
"mockery/mockery": "0.9.*@dev"
|
||||
"phpspec/prophecy": "^1.6"
|
||||
},
|
||||
"suggest": {
|
||||
"knplabs/knp-menu-bundle": "Allows easy menu integration."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -50,7 +91,7 @@
|
||||
}
|
||||
],
|
||||
"description": "Admin Theme based on the AdminLTE Template for easy integration into symfony",
|
||||
"time": "2015-12-29 23:17:55"
|
||||
"time": "2016-08-26 08:28:19"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
@@ -2681,7 +2722,9 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {
|
||||
"avanzu/admin-theme-bundle": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
||||
45
src/AppBundle/Controller/Admin/UserController.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AppBundle\Controller\Admin;
|
||||
|
||||
use AppBundle\Entity\User;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
|
||||
/**
|
||||
* Controller used to manage users in the admin part of the site.
|
||||
*
|
||||
* @Route("/admin/user")
|
||||
* @Security("has_role('ROLE_ADMIN')")
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class UserController extends Controller
|
||||
{
|
||||
/**
|
||||
* @Route("/", defaults={"page": 1}, name="admin_user")
|
||||
* @Route("/page/{page}", requirements={"page": "[1-9]\d*"}, name="admin_user_paginated")
|
||||
* @Method("GET")
|
||||
* @Cache(smaxage="10")
|
||||
*/
|
||||
public function indexAction($page)
|
||||
{
|
||||
/* @var $entries Pagerfanta */
|
||||
$entries = $this->getDoctrine()->getRepository(User::class)->findAll($page);
|
||||
|
||||
return $this->render('admin/user.html.twig', ['entries' => $entries]);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
namespace AppBundle\Controller;
|
||||
|
||||
use AppBundle\Entity\User;
|
||||
use TimesheetBundle\Entity\Activity;
|
||||
use TimesheetBundle\Entity\Project;
|
||||
use TimesheetBundle\Entity\Timesheet;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
|
||||
@@ -34,6 +37,22 @@ class DashboardController extends Controller
|
||||
*/
|
||||
public function indexAction()
|
||||
{
|
||||
return $this->render('dashboard/index.html.twig', []);
|
||||
$user = $this->getUser();
|
||||
|
||||
$timesheetRepo = $this->getDoctrine()->getRepository(Timesheet::class);
|
||||
$timesheetUserStats = $timesheetRepo->getUserStatistics($user);
|
||||
$timesheetGlobalStats = $timesheetRepo->getGlobalStatistics();
|
||||
|
||||
$activityStats = $this->getDoctrine()->getRepository(Activity::class)->getGlobalStatistics();
|
||||
$projectStats = $this->getDoctrine()->getRepository(Project::class)->getGlobalStatistics();
|
||||
$userStats = $this->getDoctrine()->getRepository(User::class)->getGlobalStatistics();
|
||||
|
||||
return $this->render('dashboard/index.html.twig', [
|
||||
'timesheetGlobal' => $timesheetGlobalStats,
|
||||
'timesheetUser' => $timesheetUserStats,
|
||||
'activity' => $activityStats,
|
||||
'project' => $projectStats,
|
||||
'user' => $userStats,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,37 +35,39 @@ class ProfileController extends Controller
|
||||
*/
|
||||
public function indexAction($ident)
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$isAdmin = false;
|
||||
if ($isAdmin) {
|
||||
} else {
|
||||
}
|
||||
|
||||
$timesheetRepo = $this->getDoctrine()->getRepository(Timesheet::class);
|
||||
$userStats = $timesheetRepo->getUserStatistics($user);
|
||||
|
||||
// FIXME fetch values dynamically and add trans filter to macros
|
||||
$items = [
|
||||
[
|
||||
'title' => 'Projects',
|
||||
'title' => 'Stundenlohn',
|
||||
'url' => '#',
|
||||
'color' => 'blue',
|
||||
'amount' => 12
|
||||
'color' => 'blue', // aqua, red, green
|
||||
'value' => 70
|
||||
],
|
||||
[
|
||||
'title' => 'Tasks',
|
||||
'title' => 'Sprache',
|
||||
'url' => '#',
|
||||
'color' => 'aqua',
|
||||
'amount' => 5
|
||||
],
|
||||
[
|
||||
'title' => 'Projects',
|
||||
'url' => '#',
|
||||
'color' => 'red',
|
||||
'amount' => 27
|
||||
],
|
||||
[
|
||||
'title' => 'Einträge',
|
||||
'url' => '#',
|
||||
'color' => 'green',
|
||||
'amount' => 842
|
||||
'color' => 'green', // aqua, red, green
|
||||
'value' => 'deutsch'
|
||||
],
|
||||
];
|
||||
|
||||
return $this->render(
|
||||
'user/profile.html.twig',
|
||||
['user' => $this->getUser(), 'items' => $items]
|
||||
[
|
||||
'user' => $this->getUser(),
|
||||
'settings' => $items,
|
||||
'stats' => $userStats
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,30 +10,37 @@ use Symfony\Component\Security\Core\User\UserInterface;
|
||||
* User
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
|
||||
* @ORM\Table(name="users", uniqueConstraints={@ORM\UniqueConstraint(name="name", columns={"name"}), @ORM\UniqueConstraint(name="apikey", columns={"apikey"})})
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="users", uniqueConstraints={@ORM\UniqueConstraint(name="name", columns={"name"})})
|
||||
*/
|
||||
class User implements UserInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(name="userID", type="integer")
|
||||
* @ORM\Column(name="id", type="integer")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="name", type="string", length=160, nullable=false, unique=true)
|
||||
*/
|
||||
private $username;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="mail", type="string", length=160, nullable=false, unique=true)
|
||||
*/
|
||||
private $email;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="password", type="string", length=254, nullable=true)
|
||||
*/
|
||||
private $password;
|
||||
@@ -45,89 +52,26 @@ class User implements UserInterface
|
||||
*/
|
||||
private $alias;
|
||||
|
||||
/**
|
||||
* FIXME remove me
|
||||
*
|
||||
* @var boolean
|
||||
*
|
||||
* @ORM\Column(name="trash", type="boolean", nullable=false)
|
||||
*/
|
||||
private $trash = '0';
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*
|
||||
* @ORM\Column(name="active", type="boolean", nullable=false)
|
||||
*/
|
||||
private $active = '1';
|
||||
private $active = true;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var \DateTime
|
||||
*
|
||||
* @ORM\Column(name="passwordResetHash", type="string", length=32, nullable=true)
|
||||
* @ORM\Column(name="start_timeframe", type="datetime", nullable=true)
|
||||
*/
|
||||
private $passwordresethash;
|
||||
private $timeframeBegin;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var \DateTime
|
||||
*
|
||||
* @ORM\Column(name="secure", type="string", length=60, nullable=false)
|
||||
* @ORM\Column(name="end_timeframe", type="datetime", nullable=true)
|
||||
*/
|
||||
private $secure = '0';
|
||||
|
||||
/**
|
||||
* FIXME manytoone relation
|
||||
*
|
||||
* @var integer
|
||||
*
|
||||
* @ORM\Column(name="lastProject", type="integer", nullable=false)
|
||||
*/
|
||||
private $lastproject = '1';
|
||||
|
||||
/**
|
||||
* FIXME manytoone
|
||||
*
|
||||
* @var integer
|
||||
*
|
||||
* @ORM\Column(name="lastActivity", type="integer", nullable=false)
|
||||
*/
|
||||
private $lastactivity = '1';
|
||||
|
||||
/**
|
||||
* FIXME manytoone
|
||||
*
|
||||
* @var integer
|
||||
*
|
||||
* @ORM\Column(name="lastRecord", type="integer", nullable=false)
|
||||
*/
|
||||
private $lastrecord = '0';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="timeframeBegin", type="string", length=60, nullable=false)
|
||||
*/
|
||||
private $timeframebegin = '0';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="timeframeEnd", type="string", length=60, nullable=false)
|
||||
*/
|
||||
private $timeframeend = '0';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="apikey", type="string", length=30, nullable=true)
|
||||
*/
|
||||
private $apikey;
|
||||
|
||||
// ======= new columns for kimai 2 =======
|
||||
/**
|
||||
* @ORM\Column(type="json_array")
|
||||
*/
|
||||
private $roles = [];
|
||||
private $timeframeEnd;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
@@ -144,11 +88,55 @@ class User implements UserInterface
|
||||
private $avatar;
|
||||
|
||||
/**
|
||||
* Set alias
|
||||
* @var string[]
|
||||
*
|
||||
* @ORM\Column(type="json_array")
|
||||
*/
|
||||
private $roles = [];
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTimeframeBegin()
|
||||
{
|
||||
return $this->timeframeBegin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $timeframeBegin
|
||||
*/
|
||||
public function setTimeframeBegin($timeframeBegin)
|
||||
{
|
||||
$this->timeframeBegin = $timeframeBegin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTimeframeEnd()
|
||||
{
|
||||
return $this->timeframeEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $timeframeEnd
|
||||
*/
|
||||
public function setTimeframeEnd($timeframeEnd)
|
||||
{
|
||||
$this->timeframeEnd = $timeframeEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $alias
|
||||
*
|
||||
* @return User
|
||||
* @return $this
|
||||
*/
|
||||
public function setAlias($alias)
|
||||
{
|
||||
@@ -158,8 +146,6 @@ class User implements UserInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get alias
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAlias()
|
||||
@@ -168,35 +154,8 @@ class User implements UserInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Set trash
|
||||
*
|
||||
* @param boolean $trash
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setTrash($trash)
|
||||
{
|
||||
$this->trash = $trash;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trash
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getTrash()
|
||||
{
|
||||
return $this->trash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set active
|
||||
*
|
||||
* @param boolean $active
|
||||
*
|
||||
* @return User
|
||||
* @return $this
|
||||
*/
|
||||
public function setActive($active)
|
||||
{
|
||||
@@ -206,8 +165,6 @@ class User implements UserInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getActive()
|
||||
@@ -216,11 +173,8 @@ class User implements UserInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Set password
|
||||
*
|
||||
* @param string $password
|
||||
*
|
||||
* @return User
|
||||
* @return $this
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
@@ -240,273 +194,82 @@ class User implements UserInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Set passwordresethash
|
||||
*
|
||||
* @param string $passwordresethash
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setPasswordresethash($passwordresethash)
|
||||
{
|
||||
$this->passwordresethash = $passwordresethash;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get passwordresethash
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPasswordresethash()
|
||||
{
|
||||
return $this->passwordresethash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ban
|
||||
*
|
||||
* @param integer $ban
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setBan($ban)
|
||||
{
|
||||
$this->ban = $ban;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ban
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getBan()
|
||||
{
|
||||
return $this->ban;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set bantime
|
||||
*
|
||||
* @param integer $bantime
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setBantime($bantime)
|
||||
{
|
||||
$this->bantime = $bantime;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bantime
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getBantime()
|
||||
{
|
||||
return $this->bantime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set secure
|
||||
*
|
||||
* @param string $secure
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setSecure($secure)
|
||||
{
|
||||
$this->secure = $secure;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get secure
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSecure()
|
||||
{
|
||||
return $this->secure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set lastproject
|
||||
*
|
||||
* @param integer $lastproject
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setLastproject($lastproject)
|
||||
{
|
||||
$this->lastproject = $lastproject;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lastproject
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getLastproject()
|
||||
{
|
||||
return $this->lastproject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set lastactivity
|
||||
*
|
||||
* @param integer $lastactivity
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setLastactivity($lastactivity)
|
||||
{
|
||||
$this->lastactivity = $lastactivity;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lastactivity
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getLastactivity()
|
||||
{
|
||||
return $this->lastactivity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set lastrecord
|
||||
*
|
||||
* @param integer $lastrecord
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setLastrecord($lastrecord)
|
||||
{
|
||||
$this->lastrecord = $lastrecord;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lastrecord
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getLastrecord()
|
||||
{
|
||||
return $this->lastrecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set timeframebegin
|
||||
*
|
||||
* @param string $timeframebegin
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setTimeframebegin($timeframebegin)
|
||||
{
|
||||
$this->timeframebegin = $timeframebegin;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timeframebegin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTimeframebegin()
|
||||
{
|
||||
return $this->timeframebegin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set timeframeend
|
||||
*
|
||||
* @param string $timeframeend
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setTimeframeend($timeframeend)
|
||||
{
|
||||
$this->timeframeend = $timeframeend;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timeframeend
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTimeframeend()
|
||||
{
|
||||
return $this->timeframeend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set apikey
|
||||
*
|
||||
* @param string $apikey
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setApikey($apikey)
|
||||
{
|
||||
$this->apikey = $apikey;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get apikey
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getApikey()
|
||||
{
|
||||
return $this->apikey;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $username
|
||||
* @return $this
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
$this->username = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEmail()
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
* @return $this
|
||||
*/
|
||||
public function setEmail($email)
|
||||
{
|
||||
$this->email = $email;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $title
|
||||
* @return $this
|
||||
*/
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAvatar()
|
||||
{
|
||||
return $this->avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $avatar
|
||||
* @return $this
|
||||
*/
|
||||
public function setAvatar($avatar)
|
||||
{
|
||||
$this->avatar = $avatar;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the roles or permissions granted to the user for security.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getRoles()
|
||||
{
|
||||
@@ -520,9 +283,14 @@ class User implements UserInterface
|
||||
return array_unique($roles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $roles
|
||||
* @return $this
|
||||
*/
|
||||
public function setRoles(array $roles)
|
||||
{
|
||||
$this->roles = $roles;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -549,35 +317,6 @@ class User implements UserInterface
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $title
|
||||
*/
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAvatar()
|
||||
{
|
||||
return $this->avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $avatar
|
||||
*/
|
||||
public function setAvatar($avatar)
|
||||
{
|
||||
$this->avatar = $avatar;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getAlias() ?: $this->getUsername();
|
||||
|
||||
@@ -61,7 +61,7 @@ class MenuBuilder
|
||||
$isAdmin = $isLoggedIn && $this->security->isGranted('ROLE_ADMIN');
|
||||
|
||||
$event->addItem(
|
||||
new MenuItemModel('dashboard', 'menu.homepage', 'dashboard', [], 'fa fa-home')
|
||||
new MenuItemModel('dashboard', 'menu.homepage', 'dashboard', [], 'fa fa-dashboard') // fa-home
|
||||
);
|
||||
|
||||
$this->eventDispatcher->dispatch(
|
||||
@@ -74,8 +74,10 @@ class MenuBuilder
|
||||
);
|
||||
|
||||
if ($isAdmin) {
|
||||
$event->addItem(
|
||||
new MenuItemModel('admin', 'menu.admin', '', [], 'fa fa-dashboard')
|
||||
$admin = new MenuItemModel('admin', 'menu.admin', '', [], 'fa fa-wrench');
|
||||
$event->addItem($admin);
|
||||
$admin->addChild(
|
||||
new MenuItemModel('user_admin', 'menu.admin_user', 'admin_user', [], 'fa fa-user')
|
||||
);
|
||||
|
||||
$this->eventDispatcher->dispatch(
|
||||
|
||||
41
src/AppBundle/Model/UserStatistic.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace AppBundle\Model;
|
||||
|
||||
/**
|
||||
* User statistics
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class UserStatistic
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $totalAmount = 0;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalAmount()
|
||||
{
|
||||
return $this->totalAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $totalAmount
|
||||
*/
|
||||
public function setTotalAmount($totalAmount)
|
||||
{
|
||||
$this->totalAmount = $totalAmount;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
@@ -11,18 +11,76 @@
|
||||
|
||||
namespace AppBundle\Repository;
|
||||
|
||||
use AppBundle\Model\UserStatistic;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
|
||||
/**
|
||||
* Class UserRepository
|
||||
*
|
||||
* This custom Doctrine repository is empty because so far we don't need any custom
|
||||
* method to query for application user information. But it's always a good practice
|
||||
* to define a custom repository that will be used when the application grows.
|
||||
* See http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes
|
||||
*
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class UserRepository extends EntityRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* Return statistic data for all user.
|
||||
*
|
||||
* @return UserStatistic
|
||||
*/
|
||||
public function getGlobalStatistics()
|
||||
{
|
||||
$countAll = $this->getEntityManager()
|
||||
->createQuery('SELECT COUNT(u.id) FROM AppBundle:User u')
|
||||
->getSingleScalarResult();
|
||||
|
||||
$stats = new UserStatistic();
|
||||
$stats->setTotalAmount($countAll);
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Query
|
||||
*/
|
||||
protected function queryAll()
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->select('u')
|
||||
->from('AppBundle:User', 'u')
|
||||
->orderBy('u.id', 'ASC');
|
||||
|
||||
return $qb->getQuery();
|
||||
}
|
||||
|
||||
public function findByUsername($username)
|
||||
{
|
||||
return $this->findOneBy(['']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $page
|
||||
*
|
||||
* @return Pagerfanta
|
||||
*/
|
||||
public function findAll($page = 1)
|
||||
{
|
||||
return $this->getPager($this->queryAll(), $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Query $query
|
||||
* @param int $page
|
||||
* @return Pagerfanta
|
||||
*/
|
||||
protected function getPager(Query $query, $page = 1)
|
||||
{
|
||||
$paginator = new Pagerfanta(new DoctrineORMAdapter($query, false));
|
||||
$paginator->setMaxPerPage(25);
|
||||
$paginator->setCurrentPage($page);
|
||||
|
||||
return $paginator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ namespace AppBundle\Twig;
|
||||
|
||||
use AppBundle\Utils\Markdown;
|
||||
use Symfony\Component\Intl\Intl;
|
||||
use DateTime;
|
||||
use DateInterval;
|
||||
|
||||
/**
|
||||
* This Twig extension adds new filters:
|
||||
* - 'md2html' to transform Markdown contents into HTML contents
|
||||
* - 'gmdate' to transform timestamps into a gmdate() formatted string
|
||||
* Multiple Twig extensions: filters and functions
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
@@ -51,7 +51,8 @@ class Extensions extends \Twig_Extension
|
||||
{
|
||||
return [
|
||||
new \Twig_SimpleFilter('md2html', [$this, 'markdownToHtml'], ['is_safe' => ['html']]),
|
||||
new \Twig_SimpleFilter('gmdate', array($this, 'gmdate')),
|
||||
new \Twig_SimpleFilter('duration', array($this, 'duration')),
|
||||
new \Twig_SimpleFilter('money', array($this, 'money')),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -66,15 +67,37 @@ class Extensions extends \Twig_Extension
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a timestamp into a gmdate() formatted string.
|
||||
* Transforms seconds into a duration string.
|
||||
*
|
||||
* @param $seconds
|
||||
* @param string $format
|
||||
* @return false|string
|
||||
* @param bool $includeSeconds
|
||||
* @return string
|
||||
*/
|
||||
public function gmdate($seconds, $format = 'H:i')
|
||||
public function duration($seconds, $includeSeconds = false)
|
||||
{
|
||||
return gmdate($format, $seconds);
|
||||
$hour = floor($seconds / 3600);
|
||||
$minute = floor(($seconds / 60) % 60);
|
||||
|
||||
$hour = $hour > 9 ? $hour : '0' . $hour;
|
||||
$minute = $minute > 9 ? $minute : '0' . $minute;
|
||||
|
||||
if (!$includeSeconds) {
|
||||
return $hour . ':' . $minute;
|
||||
}
|
||||
|
||||
$second = $seconds % 60;
|
||||
$second = $second > 9 ? $second : '0' . $second;
|
||||
|
||||
return $hour . ':' . $minute . ':' . $second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $amount
|
||||
* @return string
|
||||
*/
|
||||
public function money($amount)
|
||||
{
|
||||
return round($amount) . ' €';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,4 +42,16 @@ class TimesheetController extends Controller
|
||||
|
||||
return $this->render('TimesheetBundle:timesheet:index.html.twig', ['entries' => $entries]);
|
||||
}
|
||||
|
||||
public function statusEntryAction()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$activeEntry = $this->getDoctrine()->getRepository(Timesheet::class)->getActiveEntry($user);
|
||||
|
||||
$activeEntry = null;
|
||||
return $this->render(
|
||||
'TimesheetBundle:Sidebar:navbar-panel.html.twig',
|
||||
['entry' => $activeEntry]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ class LoadFixtures extends AppBundleLoadFixtures
|
||||
const AMOUNT_TIMESHEET = 1000; // timesheet entries total
|
||||
const AMOUNT_PROJECTS = 20; // projects entries total
|
||||
const AMOUNT_CUSTOMER = 10; // customer entries total
|
||||
const RATE_MIN = 10; // minimum rate for one hour
|
||||
const RATE_MAX = 80; // maximum rate for one hour
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -96,33 +98,33 @@ class LoadFixtures extends AppBundleLoadFixtures
|
||||
$amountUser = count($allUser);
|
||||
|
||||
$allActivity = $this->getAllActivities($manager);
|
||||
$amountActivity = count($allActivity);
|
||||
|
||||
for ($i = 0; $i <= self::AMOUNT_TIMESHEET; $i++) {
|
||||
|
||||
$startDay = round($i / 2);
|
||||
$start = new \DateTime();
|
||||
$start = $start->modify('- ' . (rand(1, 400)) . ' days');
|
||||
$start = $start->modify('- ' . (rand(1, $startDay)) . ' days');
|
||||
$start = $start->modify('- ' . (rand(1, 86400)) . ' seconds');
|
||||
|
||||
$end = clone $start;
|
||||
$end = $end->modify('+ '.(rand(1, 43200)).' seconds');
|
||||
|
||||
//$duration = $end->modify('- ' . $start->getTimestamp() . ' seconds')->getTimestamp();
|
||||
$duration = $end->getTimestamp() - $start->getTimestamp();
|
||||
$rate = rand(self::RATE_MIN, self::RATE_MAX);
|
||||
|
||||
$entry = new Timesheet();
|
||||
$entry->setActivity($allActivity[array_rand($allActivity)]);
|
||||
$entry->setStatusid(1); // TODO
|
||||
$entry->setBillable($i % 2 == 0);
|
||||
$entry->setBudget(0);
|
||||
$entry->setCleared($i % 7 == 0);
|
||||
$entry->setComment($this->getRandomPhrase());
|
||||
$entry->setDescription($this->getRandomPhrase());
|
||||
$entry->setLocation($this->getRandomLocation());
|
||||
$entry->setStart($start->getTimestamp());
|
||||
$entry->setEnd($end->getTimestamp());
|
||||
$entry->setDuration($end->modify('- ' . $start->getTimestamp() . ' seconds')->getTimestamp());
|
||||
$entry->setUser($allUser[rand(1, $amountUser)]);
|
||||
//$entry->setApproved(false); // TODO
|
||||
//$entry->setFixedrate(); // TODO
|
||||
//$entry->setRate(); // TODO
|
||||
//$entry->setTrackingnumber(); // TODO
|
||||
$entry->setRate(round(($duration / 3600) * $rate));
|
||||
$entry->setBegin($start);
|
||||
|
||||
// leave one running time entry
|
||||
if ($i < self::AMOUNT_TIMESHEET) {
|
||||
$entry->setEnd($end);
|
||||
$entry->setDuration($duration);
|
||||
}
|
||||
|
||||
$manager->persist($entry);
|
||||
}
|
||||
@@ -137,7 +139,7 @@ class LoadFixtures extends AppBundleLoadFixtures
|
||||
$entry->setName($this->getRandomProject());
|
||||
$entry->setBudget(rand(1000, 100000));
|
||||
$entry->setComment($this->getRandomPhrase());
|
||||
$entry->setCustomerId(rand(1, self::AMOUNT_CUSTOMER)); // TODO
|
||||
$entry->setCustomerId(rand(1, self::AMOUNT_CUSTOMER)); // TODO should be a user object
|
||||
$entry->setVisible($i % 3 != 0);
|
||||
|
||||
$manager->persist($entry);
|
||||
|
||||
@@ -27,11 +27,11 @@ class Customer
|
||||
/**
|
||||
* @var integer
|
||||
*
|
||||
* @ORM\Column(name="customerID", type="integer")
|
||||
* @ORM\Column(name="id", type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue(strategy="IDENTITY")
|
||||
*/
|
||||
private $customerid;
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
@@ -52,14 +52,7 @@ class Customer
|
||||
*
|
||||
* @ORM\Column(name="visible", type="boolean", nullable=false)
|
||||
*/
|
||||
private $visible = '1';
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*
|
||||
* @ORM\Column(name="filter", type="boolean", nullable=false)
|
||||
*/
|
||||
private $filter = '0';
|
||||
private $visible = true;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
@@ -153,11 +146,12 @@ class Customer
|
||||
private $timezone;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*
|
||||
* @ORM\Column(name="trash", type="boolean", nullable=false)
|
||||
* @return int
|
||||
*/
|
||||
private $trash = '0';
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set name
|
||||
@@ -183,78 +177,6 @@ class Customer
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set password
|
||||
*
|
||||
* @param string $password
|
||||
*
|
||||
* @return Customer
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->password = $password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get password
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set passwordresethash
|
||||
*
|
||||
* @param string $passwordresethash
|
||||
*
|
||||
* @return Customer
|
||||
*/
|
||||
public function setPasswordresethash($passwordresethash)
|
||||
{
|
||||
$this->passwordresethash = $passwordresethash;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get passwordresethash
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPasswordresethash()
|
||||
{
|
||||
return $this->passwordresethash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set secure
|
||||
*
|
||||
* @param string $secure
|
||||
*
|
||||
* @return Customer
|
||||
*/
|
||||
public function setSecure($secure)
|
||||
{
|
||||
$this->secure = $secure;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get secure
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSecure()
|
||||
{
|
||||
return $this->secure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set comment
|
||||
*
|
||||
@@ -303,30 +225,6 @@ class Customer
|
||||
return $this->visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set filter
|
||||
*
|
||||
* @param boolean $filter
|
||||
*
|
||||
* @return Customer
|
||||
*/
|
||||
public function setFilter($filter)
|
||||
{
|
||||
$this->filter = $filter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filter
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getFilter()
|
||||
{
|
||||
return $this->filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set company
|
||||
*
|
||||
@@ -638,38 +536,4 @@ class Customer
|
||||
{
|
||||
return $this->timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set trash
|
||||
*
|
||||
* @param boolean $trash
|
||||
*
|
||||
* @return Customer
|
||||
*/
|
||||
public function setTrash($trash)
|
||||
{
|
||||
$this->trash = $trash;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trash
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getTrash()
|
||||
{
|
||||
return $this->trash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get customerid
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getCustomerid()
|
||||
{
|
||||
return $this->customerid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class Project
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* FIXME
|
||||
* FIXME manytoone
|
||||
* @var integer
|
||||
*
|
||||
* @ORM\Column(name="customerID", type="integer", nullable=false)
|
||||
@@ -76,6 +76,16 @@ class Project
|
||||
*/
|
||||
private $activities;
|
||||
|
||||
/**
|
||||
* Get projectid
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set customerid
|
||||
*
|
||||
@@ -196,16 +206,6 @@ class Project
|
||||
return $this->budget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get projectid
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Activity[]
|
||||
*/
|
||||
|
||||
@@ -18,38 +18,48 @@ use Doctrine\ORM\Mapping as ORM;
|
||||
* Timesheet entity.
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="TimesheetBundle\Repository\TimesheetRepository")
|
||||
* @ORM\Table(name="timeSheet", indexes={@ORM\Index(columns={"userID"}), @ORM\Index(name="activity", columns={"activity"})})
|
||||
* @ORM\Table(name="timesheet", indexes={@ORM\Index(columns={"user"}), @ORM\Index(name="activity", columns={"activity"})})
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class Timesheet
|
||||
{
|
||||
/**
|
||||
* @var integer
|
||||
*
|
||||
* @ORM\Column(name="start", type="integer", nullable=false)
|
||||
*/
|
||||
private $start = '0';
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*
|
||||
* @ORM\Column(name="end", type="integer", nullable=false)
|
||||
* @ORM\Column(name="id", type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue(strategy="IDENTITY")
|
||||
*/
|
||||
private $end = '0';
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*
|
||||
* @ORM\Column(name="start_time", type="datetime", nullable=false)
|
||||
*/
|
||||
private $begin;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*
|
||||
* @ORM\Column(name="end_time", type="datetime", nullable=true)
|
||||
*/
|
||||
private $end;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*
|
||||
* @ORM\Column(name="duration", type="integer", nullable=false)
|
||||
* @ORM\Column(name="duration", type="integer", nullable=true)
|
||||
*/
|
||||
private $duration = '0';
|
||||
private $duration = 0;
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
|
||||
* @ORM\JoinColumn(name="userID", referencedColumnName="userID")
|
||||
* @ORM\JoinColumn(name="user", referencedColumnName="id")
|
||||
*/
|
||||
private $user;
|
||||
|
||||
@@ -68,142 +78,59 @@ class Timesheet
|
||||
*/
|
||||
private $description;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="comment", type="text", length=65535, nullable=true)
|
||||
*/
|
||||
private $comment;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*
|
||||
* @ORM\Column(name="commentType", type="boolean", nullable=false)
|
||||
*/
|
||||
private $commenttype = '0';
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*
|
||||
* @ORM\Column(name="cleared", type="boolean", nullable=false)
|
||||
*/
|
||||
private $cleared = '0';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="location", type="string", length=50, nullable=true)
|
||||
*/
|
||||
private $location;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="trackingNumber", type="string", length=30, nullable=true)
|
||||
*/
|
||||
private $trackingnumber;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="rate", type="decimal", precision=10, scale=2, nullable=false)
|
||||
*/
|
||||
private $rate = '0.00';
|
||||
private $rate = 0.00;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* Get entry id
|
||||
*
|
||||
* @ORM\Column(name="fixedRate", type="decimal", precision=10, scale=2, nullable=false)
|
||||
* @return integer
|
||||
*/
|
||||
private $fixedrate = '0.00';
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="budget", type="decimal", precision=10, scale=2, nullable=true)
|
||||
* @return \DateTime
|
||||
*/
|
||||
private $budget;
|
||||
public function getBegin()
|
||||
{
|
||||
return $this->begin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="approved", type="decimal", precision=10, scale=2, nullable=true)
|
||||
*/
|
||||
private $approved;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*
|
||||
* @ORM\Column(name="statusID", type="smallint", nullable=false)
|
||||
*/
|
||||
private $statusid;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*
|
||||
* @ORM\Column(name="billable", type="boolean", nullable=true)
|
||||
*/
|
||||
private $billable;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*
|
||||
* @ORM\Column(name="timeEntryID", type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue(strategy="IDENTITY")
|
||||
*/
|
||||
private $timeentryid;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set start
|
||||
*
|
||||
* @param integer $start
|
||||
*
|
||||
* @param \DateTime $begin
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setStart($start)
|
||||
public function setBegin($begin)
|
||||
{
|
||||
$this->start = $start;
|
||||
|
||||
$this->begin = $begin;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get start
|
||||
*
|
||||
* @return integer
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getStart()
|
||||
public function getEnd()
|
||||
{
|
||||
return $this->start;
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set end
|
||||
*
|
||||
* @param integer $end
|
||||
*
|
||||
* @param \DateTime $end
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setEnd($end)
|
||||
{
|
||||
$this->end = $end;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get end
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getEnd()
|
||||
{
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set duration
|
||||
*
|
||||
@@ -214,7 +141,6 @@ class Timesheet
|
||||
public function setDuration($duration)
|
||||
{
|
||||
$this->duration = $duration;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -238,7 +164,6 @@ class Timesheet
|
||||
public function setUser(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -262,7 +187,6 @@ class Timesheet
|
||||
public function setActivity($activity)
|
||||
{
|
||||
$this->activity = $activity;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -286,7 +210,6 @@ class Timesheet
|
||||
public function setDescription($description)
|
||||
{
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -300,126 +223,6 @@ class Timesheet
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set comment
|
||||
*
|
||||
* @param string $comment
|
||||
*
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setComment($comment)
|
||||
{
|
||||
$this->comment = $comment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comment
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getComment()
|
||||
{
|
||||
return $this->comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set commenttype
|
||||
*
|
||||
* @param boolean $commenttype
|
||||
*
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setCommenttype($commenttype)
|
||||
{
|
||||
$this->commenttype = $commenttype;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get commenttype
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getCommenttype()
|
||||
{
|
||||
return $this->commenttype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cleared
|
||||
*
|
||||
* @param boolean $cleared
|
||||
*
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setCleared($cleared)
|
||||
{
|
||||
$this->cleared = $cleared;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cleared
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getCleared()
|
||||
{
|
||||
return $this->cleared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set location
|
||||
*
|
||||
* @param string $location
|
||||
*
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setLocation($location)
|
||||
{
|
||||
$this->location = $location;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get location
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocation()
|
||||
{
|
||||
return $this->location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set trackingnumber
|
||||
*
|
||||
* @param string $trackingnumber
|
||||
*
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setTrackingnumber($trackingnumber)
|
||||
{
|
||||
$this->trackingnumber = $trackingnumber;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trackingnumber
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTrackingnumber()
|
||||
{
|
||||
return $this->trackingnumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set rate
|
||||
*
|
||||
@@ -430,7 +233,6 @@ class Timesheet
|
||||
public function setRate($rate)
|
||||
{
|
||||
$this->rate = $rate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -443,134 +245,4 @@ class Timesheet
|
||||
{
|
||||
return $this->rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set fixedrate
|
||||
*
|
||||
* @param string $fixedrate
|
||||
*
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setFixedrate($fixedrate)
|
||||
{
|
||||
$this->fixedrate = $fixedrate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fixedrate
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFixedrate()
|
||||
{
|
||||
return $this->fixedrate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set budget
|
||||
*
|
||||
* @param string $budget
|
||||
*
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setBudget($budget)
|
||||
{
|
||||
$this->budget = $budget;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get budget
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBudget()
|
||||
{
|
||||
return $this->budget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set approved
|
||||
*
|
||||
* @param string $approved
|
||||
*
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setApproved($approved)
|
||||
{
|
||||
$this->approved = $approved;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get approved
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getApproved()
|
||||
{
|
||||
return $this->approved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set statusid
|
||||
*
|
||||
* @param integer $statusid
|
||||
*
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setStatusid($statusid)
|
||||
{
|
||||
$this->statusid = $statusid;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get statusid
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getStatusid()
|
||||
{
|
||||
return $this->statusid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set billable
|
||||
*
|
||||
* @param boolean $billable
|
||||
*
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setBillable($billable)
|
||||
{
|
||||
$this->billable = $billable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get billable
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getBillable()
|
||||
{
|
||||
return $this->billable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timeentryid
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getTimeentryid()
|
||||
{
|
||||
return $this->timeentryid;
|
||||
}
|
||||
}
|
||||
|
||||
41
src/TimesheetBundle/Model/ActivityStatistic.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Model;
|
||||
|
||||
/**
|
||||
* Activity statistics
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class ActivityStatistic
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $totalAmount = 0;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalAmount()
|
||||
{
|
||||
return $this->totalAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $totalAmount
|
||||
*/
|
||||
public function setTotalAmount($totalAmount)
|
||||
{
|
||||
$this->totalAmount = $totalAmount;
|
||||
}
|
||||
}
|
||||
41
src/TimesheetBundle/Model/ProjectStatistic.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Model;
|
||||
|
||||
/**
|
||||
* Project statistics
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class ProjectStatistic
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $totalAmount = 0;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalAmount()
|
||||
{
|
||||
return $this->totalAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $totalAmount
|
||||
*/
|
||||
public function setTotalAmount($totalAmount)
|
||||
{
|
||||
$this->totalAmount = $totalAmount;
|
||||
}
|
||||
}
|
||||
81
src/TimesheetBundle/Model/TimesheetGlobalStatistic.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Model;
|
||||
|
||||
/**
|
||||
* Timesheet statistics
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class TimesheetGlobalStatistic extends TimesheetStatistic
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $activeThisMonth = 0;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $activeTotal = 0;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $activeCurrently = 0;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getActiveCurrently()
|
||||
{
|
||||
return $this->activeCurrently;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $activeCurrently
|
||||
*/
|
||||
public function setActiveCurrently($activeCurrently)
|
||||
{
|
||||
$this->activeCurrently = $activeCurrently;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getActiveThisMonth()
|
||||
{
|
||||
return $this->activeThisMonth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $activeThisMonth
|
||||
*/
|
||||
public function setActiveThisMonth($activeThisMonth)
|
||||
{
|
||||
$this->activeThisMonth = $activeThisMonth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getActiveTotal()
|
||||
{
|
||||
return $this->activeTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $activeTotal
|
||||
*/
|
||||
public function setActiveTotal($activeTotal)
|
||||
{
|
||||
$this->activeTotal = $activeTotal;
|
||||
}
|
||||
}
|
||||
101
src/TimesheetBundle/Model/TimesheetStatistic.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Model;
|
||||
|
||||
/**
|
||||
* Timesheet statistics
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class TimesheetStatistic
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $durationThisMonth = 0;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $durationTotal = 0;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $amountThisMonth = 0;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $amountTotal = 0;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getDurationThisMonth()
|
||||
{
|
||||
return $this->durationThisMonth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $durationThisMonth
|
||||
*/
|
||||
public function setDurationThisMonth($durationThisMonth)
|
||||
{
|
||||
$this->durationThisMonth = $durationThisMonth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getAmountTotal()
|
||||
{
|
||||
return $this->amountTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $amountTotal
|
||||
*/
|
||||
public function setAmountTotal($amountTotal)
|
||||
{
|
||||
$this->amountTotal = $amountTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getDurationTotal()
|
||||
{
|
||||
return $this->durationTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $durationTotal
|
||||
*/
|
||||
public function setDurationTotal($durationTotal)
|
||||
{
|
||||
$this->durationTotal = $durationTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getAmountThisMonth()
|
||||
{
|
||||
return $this->amountThisMonth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $amountThisMonth
|
||||
*/
|
||||
public function setAmountThisMonth($amountThisMonth)
|
||||
{
|
||||
$this->amountThisMonth = $amountThisMonth;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query;
|
||||
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use TimesheetBundle\Model\ActivityStatistic;
|
||||
|
||||
/**
|
||||
* Class ActivityRepository
|
||||
@@ -26,17 +27,48 @@ use Pagerfanta\Pagerfanta;
|
||||
class ActivityRepository extends EntityRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* Return statistic data for all user.
|
||||
*
|
||||
* @return ActivityStatistic
|
||||
*/
|
||||
public function getGlobalStatistics()
|
||||
{
|
||||
$countAll = $this->getEntityManager()
|
||||
->createQuery('SELECT COUNT(a.id) FROM TimesheetBundle:Activity a')
|
||||
->getSingleScalarResult();
|
||||
|
||||
$stats = new ActivityStatistic();
|
||||
$stats->setTotalAmount($countAll);
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @return Query
|
||||
*/
|
||||
public function queryLatest(User $user = null)
|
||||
protected function queryLatest(User $user = null)
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->select('a')
|
||||
->from('TimesheetBundle:Activity', 'a')
|
||||
->orderBy('a.name', 'DESC');
|
||||
->orderBy('a.id', 'DESC');
|
||||
|
||||
return $qb->getQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $orderBy
|
||||
* @return Query
|
||||
*/
|
||||
protected function queryAll($orderBy = 'id')
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->select('a')
|
||||
->from('TimesheetBundle:Activity', 'a')
|
||||
->orderBy('a.' . $orderBy, 'ASC');
|
||||
|
||||
return $qb->getQuery();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query;
|
||||
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use TimesheetBundle\Model\ProjectStatistic;
|
||||
|
||||
/**
|
||||
* Class ProjectRepository
|
||||
@@ -26,11 +27,27 @@ use Pagerfanta\Pagerfanta;
|
||||
class ProjectRepository extends EntityRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* Return statistic data for all user.
|
||||
*
|
||||
* @return ProjectStatistic
|
||||
*/
|
||||
public function getGlobalStatistics()
|
||||
{
|
||||
$countAll = $this->getEntityManager()
|
||||
->createQuery('SELECT COUNT(p.id) FROM TimesheetBundle:Project p')
|
||||
->getSingleScalarResult();
|
||||
|
||||
$stats = new ProjectStatistic();
|
||||
$stats->setTotalAmount($countAll);
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @return Query
|
||||
*/
|
||||
public function queryLatest(User $user = null)
|
||||
protected function queryLatest(User $user = null)
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
@@ -41,7 +58,11 @@ class ProjectRepository extends EntityRepository
|
||||
return $qb->getQuery();
|
||||
}
|
||||
|
||||
public function queryAll($orderBy = 'id')
|
||||
/**
|
||||
* @param string $orderBy
|
||||
* @return Query
|
||||
*/
|
||||
protected function queryAll($orderBy = 'id')
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
|
||||
@@ -15,8 +15,12 @@ use AppBundle\Entity\User;
|
||||
use TimesheetBundle\Entity\Timesheet;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use TimesheetBundle\Model\TimesheetGlobalStatistic;
|
||||
use TimesheetBundle\Model\TimesheetStatistic;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Class TimesheetRepository
|
||||
@@ -26,6 +30,122 @@ use Pagerfanta\Pagerfanta;
|
||||
class TimesheetRepository extends EntityRepository
|
||||
{
|
||||
|
||||
protected function queryThisMonth($select, User $user = null)
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
// FIXME
|
||||
$end = new DateTime();
|
||||
$begin = $end->sub(new \DateInterval("P30D"));
|
||||
|
||||
$qb->select($select)
|
||||
->from('TimesheetBundle:Timesheet', 't')
|
||||
->where($qb->expr()->gt('t.begin', ':from'))
|
||||
->andWhere($qb->expr()->gt('t.end', ':to'))
|
||||
->setParameter('from', $begin, Type::DATETIME)
|
||||
->setParameter('to', $end, Type::DATETIME);
|
||||
|
||||
if (null !== $user) {
|
||||
$qb->andWhere('t.user = :user')
|
||||
->setParameter('user', $user);
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
/**
|
||||
* Fetch statistic data for one user.
|
||||
*
|
||||
* @param User $user
|
||||
* @return TimesheetStatistic
|
||||
*/
|
||||
public function getUserStatistics(User $user)
|
||||
{
|
||||
$durationTotal = $this->getEntityManager()
|
||||
->createQuery('SELECT SUM(t.duration) FROM TimesheetBundle:Timesheet t WHERE t.user = :user')
|
||||
->setParameter('user', $user)
|
||||
->getSingleScalarResult();
|
||||
$rateTotal = $this->getEntityManager()
|
||||
->createQuery('SELECT SUM(t.rate) FROM TimesheetBundle:Timesheet t WHERE t.user = :user')
|
||||
->setParameter('user', $user)
|
||||
->getSingleScalarResult();
|
||||
$amountMonth = $this->queryThisMonth('SUM(t.rate)', $user)
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
$durationMonth = $this->queryThisMonth('SUM(t.duration)', $user)
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
|
||||
$stats = new TimesheetStatistic();
|
||||
$stats->setAmountTotal($rateTotal);
|
||||
$stats->setDurationTotal($durationTotal);
|
||||
$stats->setAmountThisMonth($amountMonth);
|
||||
$stats->setDurationThisMonth($durationMonth);
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch statistic data for all user.
|
||||
*
|
||||
* @return TimesheetStatistic
|
||||
*/
|
||||
public function getGlobalStatistics()
|
||||
{
|
||||
$durationTotal = $this->getEntityManager()
|
||||
->createQuery('SELECT SUM(t.duration) FROM TimesheetBundle:Timesheet t')
|
||||
->getSingleScalarResult();
|
||||
$rateTotal = $this->getEntityManager()
|
||||
->createQuery('SELECT SUM(t.rate) FROM TimesheetBundle:Timesheet t')
|
||||
->getSingleScalarResult();
|
||||
$userTotal = $this->getEntityManager()
|
||||
->createQuery('SELECT COUNT(DISTINCT(t.user)) FROM TimesheetBundle:Timesheet t')
|
||||
->getSingleScalarResult();
|
||||
$activeNow = $this->getActiveEntry();
|
||||
$amountMonth = $this->queryThisMonth('SUM(t.rate)')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
$durationMonth = $this->queryThisMonth('SUM(t.duration)')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
$activeMonth = $this->queryThisMonth('COUNT(DISTINCT(t.user))')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
|
||||
$stats = new TimesheetGlobalStatistic();
|
||||
$stats->setAmountTotal($rateTotal);
|
||||
$stats->setDurationTotal($durationTotal);
|
||||
$stats->setActiveTotal($userTotal);
|
||||
$stats->setActiveCurrently(count($activeNow));
|
||||
$stats->setActiveThisMonth($activeMonth);
|
||||
$stats->setAmountThisMonth($amountMonth);
|
||||
$stats->setDurationThisMonth($durationMonth);
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @return Query
|
||||
*/
|
||||
public function getActiveEntry(User $user = null)
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->select('t')
|
||||
->from('TimesheetBundle:Timesheet', 't')
|
||||
->where('t.begin > 0')
|
||||
->andWhere('t.end is NULL');
|
||||
|
||||
$params = [];
|
||||
|
||||
if (null !== $user) {
|
||||
$qb->andWhere('t.user = :user');
|
||||
$params['user'] = $user;
|
||||
}
|
||||
|
||||
return $qb->getQuery()->execute($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @return Query
|
||||
@@ -36,7 +156,7 @@ class TimesheetRepository extends EntityRepository
|
||||
|
||||
$qb->select('t')
|
||||
->from('TimesheetBundle:Timesheet', 't')
|
||||
->orderBy('t.start', 'DESC');
|
||||
->orderBy('t.begin', 'DESC');
|
||||
|
||||
if (null !== $user) {
|
||||
$qb->where('t.user = :user')
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{% import "AvanzuAdminThemeBundle:layout:macros.html.twig" as macro %}
|
||||
<!-- Sidebar user panel -->
|
||||
<div class="user-panel">
|
||||
<div class="pull-left image">
|
||||
<a href="#" id="ticktac">
|
||||
<img src="{{ asset('images/buzzer-on.png') }}"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="pull-left info">
|
||||
<p>10:39:05</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,37 +1,31 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% import "macros/datatables.html.twig" as tables %}
|
||||
|
||||
{% block page_title %}{{ 'admin_activity.title'|trans }}{% endblock %}
|
||||
{% block page_subtitle %}{{ 'admin_activity.subtitle'|trans }}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% if entries.count > 0 %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ 'label.id'|trans }}</th>
|
||||
<th><i class="fa fa-bookmark-o"></i> {{ 'label.name'|trans }}</th>
|
||||
<th><i class="fa fa-book"></i> {{ 'label.project'|trans }}</th>
|
||||
<th><i class="fa fa-comment-o"></i> {{ 'label.comment'|trans }}</th>
|
||||
<th><i class="fa fa-eye"></i> {{ 'label.visible'|trans }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in entries %}
|
||||
<tr>
|
||||
<td>{{ entry.id }}</td>
|
||||
<td>{{ entry.name }}</td>
|
||||
<td>{{ entry.project }}</td>
|
||||
<td>{{ entry.comment }}</td>
|
||||
<td>{{ widgets.badge_visible(entry.visible) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ tables.data_table_header({
|
||||
'label.id': '',
|
||||
'label.name': 'bookmark-o',
|
||||
'label.project': 'book',
|
||||
'label.comment': 'comment-o',
|
||||
'label.visible': 'eye',
|
||||
}) }}
|
||||
|
||||
<div class="navigation text-center">
|
||||
{{ pagerfanta(entries, 'twitter_bootstrap3_translated', { routeName: 'admin_activity_paginated' }) }}
|
||||
</div>
|
||||
{% for entry in entries %}
|
||||
<tr>
|
||||
<td>{{ entry.id }}</td>
|
||||
<td>{{ entry.name }}</td>
|
||||
<td>{{ entry.project }}</td>
|
||||
<td>{{ entry.comment }}</td>
|
||||
<td>{{ widgets.label_visible(entry.visible) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
{{ tables.data_table_footer(entries, 'admin_activity_paginated') }}
|
||||
{% else %}
|
||||
{{ widgets.callout('warning', 'error.no_entries_found') }}
|
||||
{% endif %}
|
||||
|
||||
@@ -1,39 +1,33 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% import "macros/datatables.html.twig" as tables %}
|
||||
|
||||
{% block page_title %}{{ 'admin_project.title'|trans }}{% endblock %}
|
||||
{% block page_subtitle %}{{ 'admin_project.subtitle'|trans }}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% if entries.count > 0 %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ 'label.id'|trans }}</th>
|
||||
<th><i class="fa fa-bookmark-o"></i> {{ 'label.name'|trans }}</th>
|
||||
<th><i class="fa fa-comment-o"></i> {{ 'label.comment'|trans }}</th>
|
||||
<th><i class="fa fa-eye"></i> {{ 'label.visible'|trans }}</th>
|
||||
<th><i class="fa fa-tasks"></i> {{ 'label.activity'|trans }}</th>
|
||||
<th><i class="fa fa-credit-card"></i> {{ 'label.budget'|trans }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in entries %}
|
||||
<tr>
|
||||
<td>{{ entry.id }}</td>
|
||||
<td>{{ entry.name }}</td>
|
||||
<td>{{ entry.comment }}</td>
|
||||
<td>{{ widgets.badge_visible(entry.visible) }}</td>
|
||||
<td>{{ widgets.badge_counter(entry.activities.count) }}</td>
|
||||
<td>{{ entry.budget}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ tables.data_table_header({
|
||||
'label.id': '',
|
||||
'label.name': 'bookmark-o',
|
||||
'label.comment': 'comment-o',
|
||||
'label.activity': 'tasks',
|
||||
'label.budget': 'credit-card',
|
||||
'label.visible': 'eye',
|
||||
}) }}
|
||||
|
||||
<div class="navigation text-center">
|
||||
{{ pagerfanta(entries, 'twitter_bootstrap3_translated', { routeName: 'admin_project_paginated' }) }}
|
||||
</div>
|
||||
{% for entry in entries %}
|
||||
<tr>
|
||||
<td>{{ entry.id }}</td>
|
||||
<td>{{ entry.name }}</td>
|
||||
<td>{{ entry.comment }}</td>
|
||||
<td>{{ widgets.badge_counter(entry.activities.count) }}</td>
|
||||
<td>{{ entry.budget|money}}</td>
|
||||
<td>{{ widgets.label_visible(entry.visible) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
{{ tables.data_table_footer(entries, 'admin_project_paginated') }}
|
||||
{% else %}
|
||||
{{ widgets.callout('warning', 'error.no_entries_found') }}
|
||||
{% endif %}
|
||||
|
||||
@@ -1,39 +1,32 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% import "macros/datatables.html.twig" as widgets %}
|
||||
|
||||
{% block page_title %}{{ 'admin_timesheet.title'|trans }}{% endblock %}
|
||||
{% block page_subtitle %}{{ 'admin_timesheet.subtitle'|trans }}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% if entries.count > 0 %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><i class="fa fa-calendar"></i> {{ 'label.date'|trans }}</th>
|
||||
<th><i class="fa fa-hourglass-start"></i> {{ 'label.starttime'|trans }}</th>
|
||||
<th><i class="fa fa-hourglass-end"></i> {{ 'label.endtime'|trans }}</th>
|
||||
<th><i class="fa fa-clock-o"></i> {{ 'label.duration'|trans }}</th>
|
||||
<th><i class="fa fa-user"></i> {{ 'label.username'|trans }}</th>
|
||||
<th><i class="fa fa-pencil-square-o"></i> {{ 'label.description'|trans }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in entries %}
|
||||
<tr>
|
||||
<td>{{ entry.start|date(kimai_context.date_1) }}</td>
|
||||
<td>{{ entry.start|date("H:i") }}</td>
|
||||
<td>{{ entry.end|date("H:i") }}</td>
|
||||
<td>{{ entry.duration|gmdate }}</td>
|
||||
<td>{{ entry.user.username }}</td>
|
||||
<td>{{ entry.description }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ widgets.data_table_header({
|
||||
'label.date': 'calendar',
|
||||
'label.starttime': 'hourglass-start',
|
||||
'label.endtime': 'hourglass-end',
|
||||
'label.duration': 'clock-o',
|
||||
'label.username': 'user',
|
||||
'label.description': 'pencil-square-o',
|
||||
}) }}
|
||||
|
||||
<div class="navigation text-center">
|
||||
{{ pagerfanta(entries, 'twitter_bootstrap3_translated', { routeName: 'admin_timesheet_paginated' }) }}
|
||||
</div>
|
||||
{% for entry in entries %}
|
||||
<tr>
|
||||
<td>{{ entry.begin|date(kimai_context.date_1) }}</td>
|
||||
<td>{{ entry.begin|date("H:i") }}</td>
|
||||
<td>{{ entry.end|date("H:i") }}</td>
|
||||
<td>{{ entry.duration|duration }}</td>
|
||||
<td>{{ entry.user.username }}</td>
|
||||
<td>{{ entry.description }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
{{ widgets.data_table_footer(entries, 'admin_timesheet_paginated') }}
|
||||
{% else %}
|
||||
{{ widgets.callout('warning', 'error.no_entries_found') }}
|
||||
{% endif %}
|
||||
|
||||
@@ -1,39 +1,30 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% import "macros/datatables.html.twig" as widgets %}
|
||||
|
||||
{% block page_title %}{{ 'timesheet.title'|trans }}{% endblock %}
|
||||
{% block page_subtitle %}{{ 'timesheet.subtitle'|trans }}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% if entries.count > 0 %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><i class="fa fa-calendar"></i> {{ 'label.date'|trans }}</th>
|
||||
<th><i class="fa fa-hourglass-start"></i> {{ 'label.starttime'|trans }}</th>
|
||||
<th><i class="fa fa-hourglass-end"></i> {{ 'label.endtime'|trans }}</th>
|
||||
<th><i class="fa fa-clock-o"></i> {{ 'label.duration'|trans }}</th>
|
||||
<th><i class="fa fa-user"></i> {{ 'label.username'|trans }}</th>
|
||||
<th><i class="fa fa-pencil-square-o"></i> {{ 'label.description'|trans }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in entries %}
|
||||
<tr>
|
||||
<td>{{ entry.start|date(kimai_context.date_1) }}</td>
|
||||
<td>{{ entry.start|date("H:i") }}</td>
|
||||
<td>{{ entry.end|date("H:i") }}</td>
|
||||
<td>{{ entry.duration|gmdate }}</td>
|
||||
<td>{{ entry.user.username }}</td>
|
||||
<td>{{ entry.description }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ widgets.data_table_header({
|
||||
'label.date': 'calendar',
|
||||
'label.starttime': 'hourglass-start',
|
||||
'label.endtime': 'hourglass-end',
|
||||
'label.duration': 'clock-o',
|
||||
'label.description': 'pencil-square-o',
|
||||
}) }}
|
||||
|
||||
<div class="navigation text-center">
|
||||
{{ pagerfanta(entries, 'twitter_bootstrap3_translated', { routeName: 'timesheet_paginated' }) }}
|
||||
</div>
|
||||
{% for entry in entries %}
|
||||
<tr>
|
||||
<td>{{ entry.begin|date(kimai_context.date_1) }}</td>
|
||||
<td>{{ entry.begin|date("H:i") }}</td>
|
||||
<td>{{ entry.end|date("H:i") }}</td>
|
||||
<td>{{ entry.duration|duration }}</td>
|
||||
<td>{{ entry.description }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
{{ widgets.data_table_footer(entries, 'timesheet_paginated') }}
|
||||
{% else %}
|
||||
{{ widgets.callout('warning', 'error.no_entries_found') }}
|
||||
{% endif %}
|
||||
|
||||
248
web/css/kimai.css
Normal file
@@ -0,0 +1,248 @@
|
||||
#ticktac img {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
/*
|
||||
#ticktac img {
|
||||
width: 100%;
|
||||
}
|
||||
.user-panel>.info {
|
||||
left: 80px;
|
||||
}
|
||||
*/
|
||||
footer.main-footer {
|
||||
padding: 5px;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO
|
||||
"display:hidden" regel für dataTable - spalten - mobile ansicht
|
||||
"display:hidden" regel für dataTable - header icon - mobile ansicht
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
dataTables.bootstrap.css
|
||||
*/
|
||||
div.dataTables_length label {
|
||||
font-weight: normal;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
div.dataTables_length select {
|
||||
width: 75px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.dataTables_filter {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.dataTables_filter label {
|
||||
font-weight: normal;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.dataTables_filter input {
|
||||
margin-left: 0.5em;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
div.dataTables_info {
|
||||
padding-top: 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
div.dataTables_paginate {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.dataTables_paginate ul.pagination {
|
||||
margin: 2px 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
div.dataTables_wrapper > div.row > div,
|
||||
div.dataTables_length,
|
||||
div.dataTables_filter,
|
||||
div.dataTables_info,
|
||||
div.dataTables_paginate {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.DTTT {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
table.dataTable td,
|
||||
table.dataTable th {
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
|
||||
table.dataTable {
|
||||
clear: both;
|
||||
/*margin-top: 6px !important;
|
||||
margin-bottom: 6px !important;*/
|
||||
margin-bottom: 0px !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting,
|
||||
table.dataTable thead .sorting_asc,
|
||||
table.dataTable thead .sorting_desc,
|
||||
table.dataTable thead .sorting_asc_disabled,
|
||||
table.dataTable thead .sorting_desc_disabled {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting:after,
|
||||
table.dataTable thead .sorting_asc:after,
|
||||
table.dataTable thead .sorting_desc:after {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
display: block;
|
||||
font-family: 'Glyphicons Halflings';
|
||||
opacity: 0.5;
|
||||
}
|
||||
table.dataTable thead .sorting:after {
|
||||
opacity: 0.2;
|
||||
content: "\e150"; /* sort */
|
||||
}
|
||||
table.dataTable thead .sorting_asc:after {
|
||||
content: "\e155"; /* sort-by-attributes */
|
||||
}
|
||||
table.dataTable thead .sorting_desc:after {
|
||||
content: "\e156"; /* sort-by-attributes-alt */
|
||||
}
|
||||
div.dataTables_scrollBody table.dataTable thead .sorting:after,
|
||||
div.dataTables_scrollBody table.dataTable thead .sorting_asc:after,
|
||||
div.dataTables_scrollBody table.dataTable thead .sorting_desc:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting_asc_disabled:after,
|
||||
table.dataTable thead .sorting_desc_disabled:after {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
table.dataTable thead > tr > th {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
table.dataTable th:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
||||
/* Condensed */
|
||||
table.dataTable.table-condensed thead > tr > th {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
table.dataTable.table-condensed thead .sorting:after,
|
||||
table.dataTable.table-condensed thead .sorting_asc:after,
|
||||
table.dataTable.table-condensed thead .sorting_desc:after {
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
}
|
||||
|
||||
/* Scrolling */
|
||||
div.dataTables_scrollHead table {
|
||||
margin-bottom: 0 !important;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
div.dataTables_scrollHead table thead tr:last-child th:first-child,
|
||||
div.dataTables_scrollHead table thead tr:last-child td:first-child {
|
||||
border-bottom-left-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
}
|
||||
|
||||
div.dataTables_scrollBody table {
|
||||
border-top: none;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
div.dataTables_scrollBody tbody tr:first-child th,
|
||||
div.dataTables_scrollBody tbody tr:first-child td {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
div.dataTables_scrollFoot table {
|
||||
margin-top: 0 !important;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
/* Frustratingly the border-collapse:collapse used by Bootstrap makes the column
|
||||
width calculations when using scrolling impossible to align columns. We have
|
||||
to use separate
|
||||
*/
|
||||
table.table-bordered.dataTable {
|
||||
border-collapse: separate !important;
|
||||
}
|
||||
table.table-bordered thead th,
|
||||
table.table-bordered thead td {
|
||||
border-left-width: 0;
|
||||
border-top-width: 0;
|
||||
}
|
||||
table.table-bordered tbody th,
|
||||
table.table-bordered tbody td {
|
||||
border-left-width: 0;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
table.table-bordered tfoot th,
|
||||
table.table-bordered tfoot td {
|
||||
border-left-width: 0;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
table.table-bordered th:last-child,
|
||||
table.table-bordered td:last-child {
|
||||
border-right-width: 0;
|
||||
}
|
||||
div.dataTables_scrollHead table.table-bordered {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* TableTools styles
|
||||
*/
|
||||
.table.dataTable tbody tr.active td,
|
||||
.table.dataTable tbody tr.active th {
|
||||
background-color: #08C;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.table.dataTable tbody tr.active:hover td,
|
||||
.table.dataTable tbody tr.active:hover th {
|
||||
background-color: #0075b0 !important;
|
||||
}
|
||||
|
||||
.table.dataTable tbody tr.active th > a,
|
||||
.table.dataTable tbody tr.active td > a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.table-striped.dataTable tbody tr.active:nth-child(odd) td,
|
||||
.table-striped.dataTable tbody tr.active:nth-child(odd) th {
|
||||
background-color: #017ebc;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 306 KiB After Width: | Height: | Size: 434 KiB |
BIN
web/images/buzzer-deactived.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
web/images/buzzer-off-hover.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
web/images/buzzer-off.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
web/images/buzzer-on-hover.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
web/images/buzzer-on.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
56
web/js/kimai.js
Normal file
@@ -0,0 +1,56 @@
|
||||
$(document).ready(function() {
|
||||
|
||||
(function( $ ) {
|
||||
|
||||
var pubSub = $({});
|
||||
|
||||
var methods = {
|
||||
init : function(options) {
|
||||
$.fn.kimai.settings = $.extend( {}, $.fn.kimai.defaults, options );
|
||||
},
|
||||
hook: function(name) {
|
||||
alert('Hook: ' + name);
|
||||
console.log($.fn.kimai.settings);
|
||||
},
|
||||
subscribe: function() {
|
||||
pubSub.on.apply(pubSub, arguments);
|
||||
},
|
||||
unsubscribe: function() {
|
||||
pubSub.off.apply(pubSub, arguments);
|
||||
},
|
||||
publish: function() {
|
||||
pubSub.trigger.apply(pubSub, arguments);
|
||||
},
|
||||
ticktac: function(selector) {
|
||||
$(selector + ' img').hover(function () {
|
||||
$(this).attr('src', $.fn.kimai.settings.imagePath + '/buzzer-on-hover.png');
|
||||
},function () {
|
||||
$(this).attr('src', $.fn.kimai.settings.imagePath + '/buzzer-on.png');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.kimai = function(options) {
|
||||
console.log('#');
|
||||
console.log(options);
|
||||
console.log('#');
|
||||
if (methods[options]) {
|
||||
return methods[options].apply( this, Array.prototype.slice.call(arguments, 1));
|
||||
} else if (typeof options === 'object') {
|
||||
return methods.init.apply( this, arguments );
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
// default values
|
||||
$.fn.kimai.defaults = {
|
||||
baseUrl: '/',
|
||||
imagePath: '/images'
|
||||
};
|
||||
|
||||
// once initialized, here are all values
|
||||
$.fn.kimai.settings = {};
|
||||
|
||||
}( jQuery ));
|
||||
|
||||
});
|
||||