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.
 
 
 
 
 

252 lines
14 KiB

  1. {# templates/team/index.html.twig #}
  2. {% extends 'base.html.twig' %}
  3. {% block title %}{{ 'app.team.page_title'|trans }}{% endblock %}
  4. {% block body %}
  5. <script>
  6. window.Team = {
  7. i18n: {
  8. confirmDelete: {{ 'app.team.confirm_delete'|trans|json_encode|raw }},
  9. confirmArchive: {{ 'app.team.confirm_archive'|trans|json_encode|raw }},
  10. confirmRevokeInvite:{{ 'app.team.confirm_revoke_invite'|trans|json_encode|raw }},
  11. errorSave: {{ 'app.team.error_save'|trans|json_encode|raw }},
  12. errorDelete: {{ 'app.team.error_delete'|trans|json_encode|raw }},
  13. errorArchive: {{ 'app.team.error_archive'|trans|json_encode|raw }},
  14. errorRestore: {{ 'app.team.error_restore'|trans|json_encode|raw }},
  15. errorGeneric: {{ 'app.team.error_generic'|trans|json_encode|raw }},
  16. }
  17. };
  18. </script>
  19. <div class="crud-page">
  20. <div class="crud-page__header">
  21. <h1 class="crud-page__title">{{ 'app.team.page_title'|trans }}</h1>
  22. <button class="btn btn-primary" id="team-invite-btn">{{ 'app.team.btn_new'|trans }}</button>
  23. </div>
  24. <div class="crud-tabs">
  25. <button class="crud-tab crud-tab--active" data-tab="active">
  26. {{ 'app.crud.tab_active'|trans }} ({{ activeUsers|length + pendingInvites|length }})
  27. </button>
  28. <button class="crud-tab" data-tab="archived">
  29. {{ 'app.crud.tab_archived'|trans }} ({{ archivedUsers|length }})
  30. </button>
  31. </div>
  32. {# ── Aktive User ──────────────────────────────────────────────────── #}
  33. <div class="crud-list" id="team-list" data-tab-panel="active">
  34. {% for au in activeUsers %}
  35. <div class="crud-row"
  36. id="au-{{ au.id }}"
  37. data-id="{{ au.id }}"
  38. data-first-name="{{ au.user.firstName|e('html_attr') }}"
  39. data-last-name="{{ au.user.lastName|e('html_attr') }}"
  40. data-email="{{ au.user.email|e('html_attr') }}"
  41. data-note="{{ au.user.note|default('')|e('html_attr') }}"
  42. data-role="{{ au.role }}"
  43. data-is-self="{{ au.user.id == currentUserId ? '1' : '0' }}">
  44. <div class="crud-row__display">
  45. <div class="crud-row__info">
  46. <span class="crud-row__name">{{ au.user.fullName }}</span>
  47. <span class="crud-row__meta">({{ au.roleLabelKey|trans }})</span>
  48. {% if au.user.password is null %}
  49. <span class="team-badge team-badge--pending">{{ 'app.team.invite_pending'|trans }}</span>
  50. {% endif %}
  51. </div>
  52. <div class="crud-row__actions">
  53. <button class="crud-row__btn crud-row__btn--edit"
  54. data-action="edit"
  55. title="{{ 'app.entry.btn_edit'|trans }}">
  56. {% include '_atoms/icon-edit.html.twig' %}
  57. </button>
  58. {% if au.user.id != currentUserId %}
  59. <button class="crud-row__btn crud-row__btn--delete"
  60. data-action="delete"
  61. title="{{ 'app.team.btn_remove'|trans }}">
  62. {% include '_atoms/icon-delete.html.twig' %}
  63. </button>
  64. {% endif %}
  65. </div>
  66. </div>
  67. <div class="crud-row__edit" hidden>
  68. <div class="entry-form__grid entry-form__grid--inline">
  69. <label class="entry-form__label">{{ 'app.team.label_first_name'|trans }}</label>
  70. <div class="entry-form__field">
  71. <input type="text" class="input edit-first-name"
  72. value="{{ au.user.firstName }}" />
  73. </div>
  74. <label class="entry-form__label">{{ 'app.team.label_last_name'|trans }}</label>
  75. <div class="entry-form__field">
  76. <input type="text" class="input edit-last-name"
  77. value="{{ au.user.lastName }}" />
  78. </div>
  79. <label class="entry-form__label">{{ 'app.team.label_email'|trans }}</label>
  80. <div class="entry-form__field">
  81. <input type="email" class="input edit-email"
  82. value="{{ au.user.email }}" />
  83. </div>
  84. <label class="entry-form__label">{{ 'app.crud.label_note'|trans }}</label>
  85. <div class="entry-form__field">
  86. <textarea class="textarea edit-note" rows="2">{{ au.user.note|default('') }}</textarea>
  87. </div>
  88. <label class="entry-form__label">{{ 'app.team.label_role'|trans }}</label>
  89. <div class="entry-form__field">
  90. <div class="team-role-selector{% if au.user.id == currentUserId and au.isAdmin() %} team-role-selector--disabled{% endif %}">
  91. {% set roleDisabled = (au.user.id == currentUserId and au.isAdmin()) ? 'disabled' : '' %}
  92. <label class="team-role-option">
  93. <input type="radio" class="edit-role" name="role-{{ au.id }}"
  94. value="tracker" {{ au.role == 'tracker' ? 'checked' : '' }} {{ roleDisabled }} />
  95. <span class="team-role-option__label">{{ 'app.team.role_tracker'|trans }}</span>
  96. </label>
  97. <label class="team-role-option">
  98. <input type="radio" class="edit-role" name="role-{{ au.id }}"
  99. value="member" {{ au.role == 'member' ? 'checked' : '' }} {{ roleDisabled }} />
  100. <span class="team-role-option__label">{{ 'app.team.role_member'|trans }}</span>
  101. </label>
  102. <label class="team-role-option">
  103. <input type="radio" class="edit-role" name="role-{{ au.id }}"
  104. value="admin" {{ au.role == 'admin' ? 'checked' : '' }} {{ roleDisabled }} />
  105. <span class="team-role-option__label">{{ 'app.team.role_admin'|trans }}</span>
  106. </label>
  107. </div>
  108. {% if au.user.id == currentUserId and au.isAdmin() %}
  109. <p class="team-role-hint">{{ 'app.team.role_change_disabled'|trans }}</p>
  110. {% endif %}
  111. </div>
  112. <div class="entry-form__actions">
  113. <button type="button" class="btn btn-primary" data-action="save">{{ 'app.entry.btn_save'|trans }}</button>
  114. <button type="button" class="btn btn-secondary" data-action="cancel">{{ 'app.entry.btn_cancel'|trans }}</button>
  115. </div>
  116. </div>
  117. </div>
  118. </div>
  119. {% endfor %}
  120. {# ── Ausstehende Einladungen ──────────────────────────────────── #}
  121. {% for invite in pendingInvites %}
  122. <div class="crud-row" id="invite-{{ invite.id }}">
  123. <div class="crud-row__display">
  124. <div class="crud-row__info">
  125. <span class="crud-row__name">{{ invite.firstName }} {{ invite.lastName }}</span>
  126. <span class="crud-row__meta">({{ invite.email }})</span>
  127. <span class="team-badge team-badge--pending">{{ 'app.team.invite_pending'|trans }}</span>
  128. </div>
  129. <div class="crud-row__actions">
  130. <button class="crud-row__btn crud-row__btn--delete"
  131. data-action="delete-invite"
  132. data-id="{{ invite.id }}"
  133. title="{{ 'app.team.btn_revoke_invite'|trans }}">
  134. {% include '_atoms/icon-delete.html.twig' %}
  135. </button>
  136. </div>
  137. </div>
  138. </div>
  139. {% endfor %}
  140. {% if activeUsers is empty and pendingInvites is empty %}
  141. <div class="crud-list__empty">{{ 'app.team.empty_active'|trans }}</div>
  142. {% endif %}
  143. </div>
  144. {# ── Archivierte User ─────────────────────────────────────────────── #}
  145. <div class="crud-list" id="team-list-archived" data-tab-panel="archived" hidden>
  146. {% for au in archivedUsers %}
  147. <div class="crud-row crud-row--archived" id="au-{{ au.id }}" data-id="{{ au.id }}">
  148. <div class="crud-row__display">
  149. <div class="crud-row__info">
  150. <span class="crud-row__name">{{ au.user.fullName }}</span>
  151. <span class="crud-row__meta">({{ au.roleLabelKey|trans }})</span>
  152. </div>
  153. <div class="crud-row__actions">
  154. <button class="crud-row__btn crud-row__btn--restore"
  155. data-action="unarchive"
  156. title="{{ 'app.crud.btn_restore'|trans }}">
  157. {% include '_atoms/icon-restore.html.twig' %}
  158. </button>
  159. </div>
  160. </div>
  161. </div>
  162. {% else %}
  163. <div class="crud-list__empty">{{ 'app.team.empty_archived'|trans }}</div>
  164. {% endfor %}
  165. </div>
  166. </div>
  167. {# ── Einlade-Modal ────────────────────────────────────────────────────────────── #}
  168. <div class="modal-overlay" id="team-modal" hidden>
  169. <div class="modal-card">
  170. <div class="modal-card__header">
  171. <h2 class="modal-card__title">{{ 'app.team.modal_title'|trans }}</h2>
  172. <button class="modal-card__close" id="team-modal-close" type="button">
  173. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  174. <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
  175. </svg>
  176. </button>
  177. </div>
  178. <div id="team-modal-errors" class="form-errors" hidden></div>
  179. <div class="modal-card__body">
  180. <div class="form-row">
  181. <div class="form-field">
  182. <label class="form-field__label" for="inv-firstName">{{ 'app.team.label_first_name'|trans }}</label>
  183. <input class="input" type="text" id="inv-firstName" autocomplete="off" />
  184. </div>
  185. <div class="form-field">
  186. <label class="form-field__label" for="inv-lastName">{{ 'app.team.label_last_name'|trans }}</label>
  187. <input class="input" type="text" id="inv-lastName" autocomplete="off" />
  188. </div>
  189. </div>
  190. <div class="form-field">
  191. <label class="form-field__label" for="inv-email">{{ 'app.team.label_email'|trans }}</label>
  192. <input class="input" type="email" id="inv-email" autocomplete="off" />
  193. </div>
  194. <div class="form-field">
  195. <label class="form-field__label">{{ 'app.team.label_role'|trans }}</label>
  196. <div class="team-role-selector">
  197. <label class="team-role-option">
  198. <input type="radio" name="inv-role" value="tracker" />
  199. <span class="team-role-option__label">{{ 'app.team.role_tracker'|trans }}</span>
  200. </label>
  201. <label class="team-role-option">
  202. <input type="radio" name="inv-role" value="member" checked />
  203. <span class="team-role-option__label">{{ 'app.team.role_member'|trans }}</span>
  204. </label>
  205. <label class="team-role-option">
  206. <input type="radio" name="inv-role" value="admin" />
  207. <span class="team-role-option__label">{{ 'app.team.role_admin'|trans }}</span>
  208. </label>
  209. </div>
  210. </div>
  211. </div>
  212. <div class="modal-card__footer">
  213. <button class="btn btn-secondary" id="team-modal-cancel" type="button">{{ 'app.entry.btn_cancel'|trans }}</button>
  214. <button class="btn btn-cta" id="team-modal-submit" type="button">{{ 'app.team.btn_invite'|trans }}</button>
  215. </div>
  216. </div>
  217. </div>
  218. {% endblock %}
  219. {% block javascripts %}
  220. {{ parent() }}
  221. {{ encore_entry_script_tags('team') }}
  222. {% endblock %}