|
- {# templates/report/times.html.twig #}
- {% extends 'base.html.twig' %}
-
- {% set monthsShort = deMonthsShort() %}
-
- {% block title %}{{ 'app.report.page_title'|trans }}{% endblock %}
-
- {% block javascripts %}
- {{ parent() }}
- {{ encore_entry_script_tags('report') }}
- {% endblock %}
-
- {% block body %}
-
- <script>
- window.Report = {
- trackingInterval: {{ trackingInterval }},
- currentUserId: {{ currentUserId }},
- isAdmin: {{ isAdmin ? 'true' : 'false' }},
- isTracker: {{ isTracker ? 'true' : 'false' }},
- limit: {{ limit }},
- clients: [
- {% for client in clients %}
- { id: {{ client.id }}, name: {{ client.name|json_encode|raw }} }{% if not loop.last %},{% endif %}
- {% endfor %}
- ],
- projects: [
- {% for project in projects %}
- {
- id: {{ project.id }},
- name: {{ project.name|json_encode|raw }},
- clientName: {{ project.client.name|json_encode|raw }} }{% if not loop.last %},{% endif %}
- {% endfor %}
- ],
- services: [
- {% for service in services %}
- {
- id: {{ service.id }},
- name: {{ service.name|json_encode|raw }},
- billable: {{ service.billable ? 'true' : 'false' }} }{% if not loop.last %},{% endif %}
- {% endfor %}
- ],
- users: {{ userList|json_encode|raw }},
- i18n: {
- btnSave: {{ 'app.entry.btn_save'|trans|json_encode|raw }},
- btnCancel: {{ 'app.entry.btn_cancel'|trans|json_encode|raw }},
- btnEdit: {{ 'app.entry.btn_edit'|trans|json_encode|raw }},
- btnDelete: {{ 'app.entry.btn_delete'|trans|json_encode|raw }},
- confirmDelete: {{ 'app.entry.confirm_delete'|trans|json_encode|raw }},
- errorSave: {{ 'app.entry.error_save'|trans|json_encode|raw }},
- errorDelete: {{ 'app.entry.error_delete'|trans|json_encode|raw }},
- errorNoProject: {{ 'app.entry.error_no_project'|trans|json_encode|raw }},
- errorZeroDuration: {{ 'app.entry.error_zero_duration'|trans|json_encode|raw }},
- errorDurationTooLong:{{ 'app.entry.error_duration_too_long'|trans|json_encode|raw }},
- warnDurationLong: {{ 'app.entry.warn_duration_long'|trans|json_encode|raw }},
- billable: {{ 'app.service.billable'|trans|json_encode|raw }},
- notBillable: {{ 'app.service.not_billable'|trans|json_encode|raw }},
- selectPh: {{ 'app.entry.select_placeholder'|trans|json_encode|raw }},
- btnLock: {{ 'app.report.btn_lock'|trans|json_encode|raw }},
- btnUnlock: {{ 'app.report.btn_unlock'|trans|json_encode|raw }},
- }
- };
- </script>
-
- <div class="report-page">
-
- <div class="report-header">
- <h1 class="report-header__title">{{ 'app.report.heading'|trans }}</h1>
-
- <div class="report-header__right">
- <span class="report-account-name">
- <svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" class="report-account-name__icon">
- <path d="M10 11a4 4 0 100-8 4 4 0 000 8zM3 17a7 7 0 0114 0" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
- </svg>
- {{ accountName }}
- </span>
-
- <nav class="account-tabs">
- <a href="{{ path('report_times') }}"
- class="account-tab account-tab--active">
- {{ 'app.report.tab_times'|trans }}
- </a>
- <span class="account-tab account-tab--disabled">
- {{ 'app.report.tab_projects'|trans }}
- </span>
- </nav>
- </div>
- </div>
-
- <div class="report-content">
- <div class="report-card">
-
- {# ── Toolbar ──────────────────────────────────────────────────────── #}
- <div class="report-toolbar">
- <div class="report-toolbar__left">
- <button class="report-toolbar__action{% if filterActive %} report-toolbar__action--active{% endif %}"
- id="btn-filter-toggle"
- type="button">
- <svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
- <circle cx="6.5" cy="6.5" r="4" stroke="currentColor" stroke-width="1.3"/>
- <path d="M11 11l2.5 2.5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
- </svg>
- {{ 'app.report.toolbar_filter'|trans }}
- </button>
- </div>
- </div>
-
- {# ── Filter-Panel ─────────────────────────────────────────────────── #}
- {% include 'report/_filter-panel.html.twig' %}
-
- {# ── Tabellen-Header ───────────────────────────────────────────────── #}
- <div class="report-table">
- <div class="report-table__head">
- <div class="report-table__cell report-table__cell--date">
- {{ 'app.report.col_date'|trans }}
- <span class="report-table__sort-icon">▾</span>
- </div>
- <div class="report-table__cell report-table__cell--client">{{ 'app.report.col_client'|trans }}</div>
- <div class="report-table__cell report-table__cell--project">{{ 'app.report.col_project'|trans }}</div>
- <div class="report-table__cell report-table__cell--service">{{ 'app.report.col_service'|trans }}</div>
- <div class="report-table__cell report-table__cell--user">{{ 'app.report.col_user'|trans }}</div>
- <div class="report-table__cell report-table__cell--note">{{ 'app.report.col_note'|trans }}</div>
- <div class="report-table__cell report-table__cell--duration">
- {{ 'app.report.col_hours'|trans }}
- <span class="report-table__summary">{{ totalDuration }}</span>
- </div>
- <div class="report-table__cell report-table__cell--revenue">
- {{ 'app.report.col_revenue'|trans }}
- <span class="report-table__summary">{{ totalRevenue|number_format(2, ',', '.') }} €</span>
- </div>
- <div class="report-table__cell report-table__cell--actions"></div>
- </div>
-
- {# ── Einträge ──────────────────────────────────────────────────── #}
- {% for entry in entries %}
- {% set service = entry.service %}
- {% set billable = (service is null or service.billable) %}
- {% set hourlyRate = entry.project.client.hourlyRate %}
- {% set monthShort = monthsShort[entry.date|date('n') - 1] %}
- {% set canEdit = isAdmin or (entry.userId == currentUserId) %}
-
- <div class="report-table__row{% if entry.invoiced %} report-table__row--invoiced{% endif %}"
- data-entry-id="{{ entry.id }}"
- data-user-id="{{ entry.userId }}"
- data-project-id="{{ entry.project.id }}"
- data-service-id="{{ entry.service ? entry.service.id : '' }}"
- data-duration="{{ entry.duration }}"
- data-note="{{ entry.note|default('')|e('html_attr') }}"
- data-invoiced="{{ entry.invoiced ? 'true' : 'false' }}">
-
- <div class="report-table__cell report-table__cell--date">
- {{ entry.date|date('j') }}. {{ monthShort }} {{ entry.date|date('y') }}
- </div>
-
- <div class="report-table__cell report-table__cell--client">
- {{ entry.project.client.name }}
- </div>
-
- <div class="report-table__cell report-table__cell--project">
- {{ entry.project.name }}
- </div>
-
- <div class="report-table__cell report-table__cell--service">
- {{ service ? service.name : '' }}
- </div>
-
- <div class="report-table__cell report-table__cell--user">
- {{ userMap[entry.userId] ?? ('User #' ~ entry.userId) }}
- </div>
-
- <div class="report-table__cell report-table__cell--note">
- {{ entry.note }}
- </div>
-
- <div class="report-table__cell report-table__cell--duration">
- {{ entry.durationFormatted }}
- </div>
-
- <div class="report-table__cell report-table__cell--revenue">
- {% if billable and hourlyRate is not null %}
- {{ (hourlyRate * entry.duration / 60)|number_format(2, ',', '.') }} €
- {% endif %}
- </div>
-
- <div class="report-table__cell report-table__cell--actions">
- {% if canEdit and not entry.invoiced %}
- <button class="report-action-btn report-action-btn--edit"
- data-action="edit"
- title="{{ 'app.entry.btn_edit'|trans }}">
- {% include '_atoms/icon-edit.html.twig' %}
- </button>
- <button class="report-action-btn report-action-btn--delete"
- data-action="delete"
- title="{{ 'app.entry.btn_delete'|trans }}">
- {% include '_atoms/icon-delete.html.twig' %}
- </button>
- {% endif %}
- {% if canEdit %}
- <button class="report-lock{% if entry.invoiced %} report-lock--invoiced{% endif %}"
- data-action="toggle-invoiced"
- title="{{ entry.invoiced ? 'app.report.btn_unlock'|trans : 'app.report.btn_lock'|trans }}">
- {% include '_atoms/icon-lock.html.twig' %}
- </button>
- {% endif %}
- </div>
-
- {# ── Inline-Edit-Formular ───────────────────────────────── #}
- {% if canEdit and not entry.invoiced %}
- <div class="report-row__edit" hidden>
- <div class="report-row__edit-grid">
-
- <label class="report-row__edit-label">{{ 'app.entry.label_duration'|trans }}</label>
- <div class="report-row__edit-field">
- <input type="text"
- class="input input--sm edit-duration"
- value="{{ entry.durationFormatted }}"
- autocomplete="off" />
- {% include '_atoms/duration-help.html.twig' %}
- </div>
-
- <label class="report-row__edit-label">{{ 'app.entry.label_project_service'|trans }}</label>
- <div class="report-row__edit-field report-row__edit-field--selects">
- <select class="select edit-project"></select>
- <select class="select edit-service"></select>
- </div>
-
- <label class="report-row__edit-label">{{ 'app.entry.label_note'|trans }}</label>
- <div class="report-row__edit-field">
- <textarea class="textarea edit-note" rows="2">{{ entry.note|default('') }}</textarea>
- </div>
-
- <div class="report-row__edit-actions">
- <button type="button" class="btn btn-primary" data-action="save">
- {{ 'app.entry.btn_save'|trans }}
- </button>
- <button type="button" class="btn btn-secondary" data-action="cancel">
- {{ 'app.entry.btn_cancel'|trans }}
- </button>
- </div>
-
- </div>
- </div>
- {% endif %}
-
- </div>
- {% else %}
- <div class="report-table__empty">{{ 'app.report.no_entries'|trans }}</div>
- {% endfor %}
-
- {# ── Pagination-Footer ─────────────────────────────────────────── #}
- <div class="report-pagination">
- <div class="report-pagination__limits">
- {{ 'app.report.show'|trans }}
- {% for l in validLimits %}
- {% if l == limit %}
- <strong>{{ l }}</strong>
- {% else %}
- <a href="{{ path('report_times', {limit: l}) }}">{{ l }}</a>
- {% endif %}
- {% endfor %}
- {{ 'app.report.of_total'|trans({'%count%': totalCount|number_format(0, ',', '.')}) }}
- </div>
- <span class="report-pagination__duration">{{ totalDuration }}</span>
- <span class="report-pagination__revenue">{{ totalRevenue|number_format(2, ',', '.') }} €</span>
- <span class="report-pagination__lock-spacer"></span>
- </div>
-
- </div>{# /.report-table #}
-
- </div>{# /.report-card #}
- </div>{# /.report-content #}
-
- </div>{# /.report-page #}
-
- {% endblock %}
|