You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

200 lines
9.0 KiB

  1. {# templates/timetracking/week.html.twig #}
  2. {% extends 'base.html.twig' %}
  3. {% from '_macros/helpers.html.twig' import smart_date %}
  4. {% set months = deMonths() %}
  5. {% set monthsShort = deMonthsShort() %}
  6. {% set weekdays = deWeekdays() %}
  7. {% set weekdaysShort= deWeekdaysShort() %}
  8. {% block title %}
  9. {{ smart_date(currentDate, todayStr, tomorrowStr, yesterdayStr, months, weekdays) }}
  10. {% endblock %}
  11. {% block body %}
  12. <script>
  13. window.TT = {
  14. activeDate: '{{ currentDate|date('Y-m-d') }}',
  15. trackingInterval: {{ trackingInterval }},
  16. projects: [
  17. {% for project in projects %}
  18. { id: {{ project.id }}, name: {{ project.name|json_encode|raw }}, clientName: {{ project.client.name|json_encode|raw }} }{% if not loop.last %},{% endif %}
  19. {% endfor %}
  20. ],
  21. services: [
  22. {% for service in services %}
  23. { id: {{ service.id }}, name: {{ service.name|json_encode|raw }}, billable: {{ service.billable ? 'true' : 'false' }} }{% if not loop.last %},{% endif %}
  24. {% endfor %}
  25. ],
  26. i18n: {
  27. today: {{ 'app.date.today'|trans|json_encode|raw }},
  28. tomorrow: {{ 'app.date.tomorrow'|trans|json_encode|raw }},
  29. yesterday: {{ 'app.date.yesterday'|trans|json_encode|raw }},
  30. weekLabel: {{ 'app.date.week_label'|trans|json_encode|raw }},
  31. months: {{ months|json_encode|raw }},
  32. monthsShort: {{ monthsShort|json_encode|raw }},
  33. weekdays: {{ weekdays|json_encode|raw }},
  34. weekdaysShort: {{ weekdaysShort|json_encode|raw }},
  35. prevWeek: {{ 'app.nav.prev_week'|trans|json_encode|raw }},
  36. nextWeek: {{ 'app.nav.next_week'|trans|json_encode|raw }},
  37. monthView: {{ 'app.nav.month_view'|trans|json_encode|raw }},
  38. prevMonth: {{ 'app.nav.prev_month'|trans|json_encode|raw }},
  39. nextMonth: {{ 'app.nav.next_month'|trans|json_encode|raw }},
  40. billable: {{ 'app.service.billable'|trans|json_encode|raw }},
  41. notBillable: {{ 'app.service.not_billable'|trans|json_encode|raw }},
  42. selectPh: {{ 'app.entry.select_placeholder'|trans|json_encode|raw }},
  43. noEntries: {{ 'app.entry.no_entries'|trans|json_encode|raw }},
  44. btnSave: {{ 'app.entry.btn_save'|trans|json_encode|raw }},
  45. btnCancel: {{ 'app.entry.btn_cancel'|trans|json_encode|raw }},
  46. btnEdit: {{ 'app.entry.btn_edit'|trans|json_encode|raw }},
  47. btnDelete: {{ 'app.entry.btn_delete'|trans|json_encode|raw }},
  48. labelDuration: {{ 'app.entry.label_duration'|trans|json_encode|raw }},
  49. labelProjectService: {{ 'app.entry.label_project_service'|trans|json_encode|raw }},
  50. labelLabel: {{ 'app.entry.label_label'|trans|json_encode|raw }},
  51. placeholderLabel: {{ 'app.entry.placeholder_label'|trans|json_encode|raw }},
  52. labelNote: {{ 'app.entry.label_note'|trans|json_encode|raw }},
  53. confirmDelete: {{ 'app.entry.confirm_delete'|trans|json_encode|raw }},
  54. errorNoProject: {{ 'app.entry.error_no_project'|trans|json_encode|raw }},
  55. errorSave: {{ 'app.entry.error_save'|trans|json_encode|raw }},
  56. errorDelete: {{ 'app.entry.error_delete'|trans|json_encode|raw }},
  57. errorLoad: {{ 'app.entry.error_load'|trans|json_encode|raw }},
  58. durationHint: {{ 'app.entry.duration_hint'|trans|json_encode|raw }},
  59. errorZeroDuration: {{ 'app.entry.error_zero_duration'|trans|json_encode|raw }},
  60. errorDurationTooLong: {{ 'app.entry.error_duration_too_long'|trans|json_encode|raw }},
  61. errorDailyLimitExceeded: {{ 'app.entry.error_daily_limit_exceeded'|trans|json_encode|raw }},
  62. warnDurationLong: {{ 'app.entry.warn_duration_long'|trans|json_encode|raw }},
  63. invoicedTitle: {{ 'app.entry.invoiced_title'|trans|json_encode|raw }},
  64. noteShow: {{ 'app.entry.note_show'|trans|json_encode|raw }},
  65. noteHide: {{ 'app.entry.note_hide'|trans|json_encode|raw }},
  66. btnTimerToggle: {{ 'app.stopwatch.resume'|trans|json_encode|raw }},
  67. confirmTimerReplace: {{ 'app.stopwatch.confirm_replace'|trans|json_encode|raw }},
  68. },
  69. };
  70. </script>
  71. <div class="tt-page">
  72. {% include '_sections/tt-header.html.twig' %}
  73. <div class="greeting">
  74. <span class="greeting__text">{{ greeting }}, {{ firstName }}!</span>
  75. </div>
  76. <main class="tt-content">
  77. <div class="entry-form" id="entry-form">
  78. <div class="entry-form__grid">
  79. <label class="entry-form__label">{{ 'app.entry.label_duration'|trans }}</label>
  80. <div class="entry-form__field">
  81. <input type="text" id="create-duration" class="input input--sm" value="0:00" autocomplete="off" />
  82. {% include '_atoms/duration-help.html.twig' %}
  83. </div>
  84. <label class="entry-form__label">{{ 'app.entry.label_project'|trans }}</label>
  85. <div class="entry-form__field">
  86. <select id="create-project" class="select">
  87. <option value="">{{ 'app.entry.select_placeholder'|trans }}</option>
  88. {% set currentClient = null %}
  89. {% for project in projects %}
  90. {% if project.client.name != currentClient %}
  91. {% if currentClient is not null %}</optgroup>{% endif %}
  92. <optgroup label="{{ project.client.name }}">
  93. {% set currentClient = project.client.name %}
  94. {% endif %}
  95. <option value="{{ project.id }}">{{ project.name }}</option>
  96. {% endfor %}
  97. {% if currentClient is not null %}</optgroup>{% endif %}
  98. </select>
  99. </div>
  100. <label class="entry-form__label">{{ 'app.entry.label_service'|trans }}</label>
  101. <div class="entry-form__field">
  102. <select id="create-service" class="select">
  103. <option value="">{{ 'app.entry.select_placeholder'|trans }}</option>
  104. {% set currentGroup = null %}
  105. {% for service in services %}
  106. {% set group = service.billable ? 'app.service.billable'|trans : 'app.service.not_billable'|trans %}
  107. {% if group != currentGroup %}
  108. {% if currentGroup is not null %}</optgroup>{% endif %}
  109. <optgroup label="{{ group }}">
  110. {% set currentGroup = group %}
  111. {% endif %}
  112. <option value="{{ service.id }}">{{ service.name }}</option>
  113. {% endfor %}
  114. {% if currentGroup is not null %}</optgroup>{% endif %}
  115. </select>
  116. </div>
  117. <label class="entry-form__label">{{ 'app.entry.label_label'|trans }}</label>
  118. <div class="entry-form__field entry-form__field--label">
  119. <div class="label-chips" id="create-label-chips"></div>
  120. <div class="label-input-wrap">
  121. <input type="text" id="create-label" class="input input--sm"
  122. placeholder="{{ 'app.entry.placeholder_label'|trans }}" autocomplete="off" />
  123. <div class="label-autocomplete" id="create-label-autocomplete" hidden></div>
  124. </div>
  125. </div>
  126. <label class="entry-form__label entry-form__label--note">{{ 'app.entry.label_note'|trans }}</label>
  127. <div class="entry-form__field entry-form__field--note">
  128. <textarea id="create-note" class="textarea" rows="3"
  129. placeholder="{{ 'app.entry.placeholder_note'|trans }}"></textarea>
  130. </div>
  131. {# Minimal-Modus: Bemerkung-Toggle (nur via CSS/JS sichtbar) #}
  132. <div class="entry-form__note-toggle-row">
  133. <button type="button" class="entry-form__note-toggle" id="btn-note-toggle">
  134. {{ 'app.entry.note_show'|trans }}
  135. </button>
  136. </div>
  137. <div class="entry-form__actions">
  138. <button type="button" class="btn btn-primary" id="btn-create">
  139. {{ 'app.entry.btn_create'|trans }}
  140. </button>
  141. </div>
  142. </div>
  143. </div>
  144. {# Minimal-Modus: Summary-Zeile zum Aufklappen #}
  145. {% if timeEntries is not empty %}
  146. <div class="entry-list__summary" id="entry-list-summary">
  147. <button type="button" class="entry-list__summary-btn" id="btn-entries-toggle">
  148. <span class="entry-list__summary-count">{{ timeEntries|length }} {{ timeEntries|length == 1 ? 'app.entry.count_one'|trans : 'app.entry.count_other'|trans }}</span>
  149. <span class="entry-list__summary-sep">·</span>
  150. <span class="entry-list__summary-total">{{ totalDuration }}</span>
  151. <span class="entry-list__summary-arrow">▾</span>
  152. </button>
  153. </div>
  154. {% endif %}
  155. <div class="entry-list" id="entry-list" data-date="{{ currentDate|date('Y-m-d') }}">
  156. {% if timeEntries is empty %}
  157. <div class="empty-state" id="empty-state">
  158. <p class="empty-state__title">{{ 'app.entry.no_entries'|trans }}</p>
  159. </div>
  160. {% else %}
  161. <div class="entry-list__items" id="entry-items">
  162. {% for entry in timeEntries %}
  163. {% include 'timetracking/_entry_row.html.twig' with { entry: entry } %}
  164. {% endfor %}
  165. </div>
  166. <div class="entry-list__footer" id="entry-footer">
  167. <span class="entry-list__total">{{ totalDuration }}</span>
  168. </div>
  169. {% endif %}
  170. </div>
  171. </main>
  172. </div>
  173. {% endblock %}
  174. {% block javascripts %}
  175. {{ parent() }}
  176. {% endblock %}