libreqda 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. CHANGELOG.md +481 -0
  2. libreqda-1.2.0.dist-info/LICENSE.txt +28 -0
  3. libreqda-1.2.0.dist-info/METADATA +131 -0
  4. libreqda-1.2.0.dist-info/RECORD +255 -0
  5. libreqda-1.2.0.dist-info/WHEEL +4 -0
  6. libreqda-1.2.0.dist-info/entry_points.txt +4 -0
  7. scripts/babelrc +8 -0
  8. scripts/dist/LICENSE.txt +33 -0
  9. scripts/dist/RELEASE.txt +23 -0
  10. scripts/dist/macos/libreqda.icns +0 -0
  11. scripts/dist/macos/macos.spec +43 -0
  12. scripts/dist/macos/plist.patch +10 -0
  13. scripts/dist/macos/taguette.icns +0 -0
  14. scripts/dist/macos/taguette_console_wrapper +4 -0
  15. scripts/dist/macos.sh +32 -0
  16. scripts/dist/pyinstaller_entrypoint.py +19 -0
  17. scripts/dist/windows/windows-installer.iss +51 -0
  18. scripts/dist/windows/windows.spec +39 -0
  19. scripts/edugain/worker_update_edugain_idps.py +89 -0
  20. scripts/edugain/worker_update_edugain_idps.sh +9 -0
  21. scripts/make_password_reset_link.py +56 -0
  22. scripts/make_theme.py +99 -0
  23. scripts/new_db_revision.sh +8 -0
  24. scripts/register_user.py +76 -0
  25. scripts/update_pot.sh +33 -0
  26. scripts/update_translations.sh +17 -0
  27. scripts/validate.py +140 -0
  28. scripts/wipe_and_disable_user.py +90 -0
  29. taguette/__init__.py +13 -0
  30. taguette/access_control.py +145 -0
  31. taguette/convert.py +623 -0
  32. taguette/database/__init__.py +177 -0
  33. taguette/database/base.py +9 -0
  34. taguette/database/copy.py +433 -0
  35. taguette/database/models.py +920 -0
  36. taguette/demodata.py +688 -0
  37. taguette/export.py +500 -0
  38. taguette/extract.py +287 -0
  39. taguette/import_codebook.py +110 -0
  40. taguette/l10n/de_DE/LC_MESSAGES/taguette_javascript.mo +0 -0
  41. taguette/l10n/de_DE/LC_MESSAGES/taguette_main.mo +0 -0
  42. taguette/l10n/el_EL/LC_MESSAGES/taguette_javascript.mo +0 -0
  43. taguette/l10n/el_GR/LC_MESSAGES/taguette_javascript.mo +0 -0
  44. taguette/l10n/el_GR/LC_MESSAGES/taguette_main.mo +0 -0
  45. taguette/l10n/en_US/LC_MESSAGES/taguette_javascript.mo +0 -0
  46. taguette/l10n/en_US/LC_MESSAGES/taguette_main.mo +0 -0
  47. taguette/l10n/es_ES/LC_MESSAGES/taguette_javascript.mo +0 -0
  48. taguette/l10n/es_ES/LC_MESSAGES/taguette_main.mo +0 -0
  49. taguette/l10n/fr_FR/LC_MESSAGES/taguette_javascript.mo +0 -0
  50. taguette/l10n/fr_FR/LC_MESSAGES/taguette_main.mo +0 -0
  51. taguette/l10n/id_ID/LC_MESSAGES/taguette_javascript.mo +0 -0
  52. taguette/l10n/id_ID/LC_MESSAGES/taguette_main.mo +0 -0
  53. taguette/l10n/it_IT/LC_MESSAGES/taguette_javascript.mo +0 -0
  54. taguette/l10n/it_IT/LC_MESSAGES/taguette_main.mo +0 -0
  55. taguette/l10n/nl_NL/LC_MESSAGES/taguette_javascript.mo +0 -0
  56. taguette/l10n/nl_NL/LC_MESSAGES/taguette_main.mo +0 -0
  57. taguette/l10n/pt_PT/LC_MESSAGES/taguette_javascript.mo +0 -0
  58. taguette/l10n/pt_PT/LC_MESSAGES/taguette_main.mo +0 -0
  59. taguette/l10n/sv_SE/LC_MESSAGES/taguette_javascript.mo +0 -0
  60. taguette/l10n/sv_SE/LC_MESSAGES/taguette_main.mo +0 -0
  61. taguette/loki_handler.py +107 -0
  62. taguette/main.py +649 -0
  63. taguette/migrations/env.py +72 -0
  64. taguette/migrations/script.py.mako +24 -0
  65. taguette/migrations/versions/09c662cd9483_make_documents_filename_not_null.py +39 -0
  66. taguette/migrations/versions/1b29e68900c1_add_user_roles.py +55 -0
  67. taguette/migrations/versions/1b96a606551d_update_user_to_have_a_display_name.py +34 -0
  68. taguette/migrations/versions/2467bab602b7_add_color_column_to_tags_table.py +33 -0
  69. taguette/migrations/versions/382db36f20f4_add_user_disabled.py +66 -0
  70. taguette/migrations/versions/43d6c240309d_remove_document_groups.py +81 -0
  71. taguette/migrations/versions/447d636f72c5_cascades.py +40 -0
  72. taguette/migrations/versions/491de2dc7cd7_add_indexes.py +51 -0
  73. taguette/migrations/versions/4c55b3b3ff30_user_login_length.py +58 -0
  74. taguette/migrations/versions/61a4abcc8769_user_with_parent_for_permissions.py +40 -0
  75. taguette/migrations/versions/6489b5f9cfb5_increase_password_hash_size.py +34 -0
  76. taguette/migrations/versions/679f625e6e6a_fix_unique_tags.py +59 -0
  77. taguette/migrations/versions/77c4543a5b9b_add_parent_id_column_to_collection_table.py +41 -0
  78. taguette/migrations/versions/7c94b7a31e69_add_is_archived_column_to_projets.py +39 -0
  79. taguette/migrations/versions/7f56de97e3c8_change_edugain_displayname_length.py +42 -0
  80. taguette/migrations/versions/807c0cc0ecf6_remove_invalid_hltags.py +43 -0
  81. taguette/migrations/versions/80b1cc9d4c22_add_user_email.py +35 -0
  82. taguette/migrations/versions/91ade71ccf4d_add_user_language.py +28 -0
  83. taguette/migrations/versions/955db2e523d1_add_collections_tables_and_associations_.py +60 -0
  84. taguette/migrations/versions/a1b2c3d4e5f6_add_user_hide_code_counts.py +28 -0
  85. taguette/migrations/versions/b23f3b7a638e_add_user_password_change_date.py +36 -0
  86. taguette/migrations/versions/b7c1e2d3f4a5_add_project_compact_layout.py +28 -0
  87. taguette/migrations/versions/bc8e0e0677e9_rename_command_fields.py +126 -0
  88. taguette/migrations/versions/bce44849c2f2_lowercase_logins.py +96 -0
  89. taguette/migrations/versions/ca076140922f_update_user_for_external_logins.py +32 -0
  90. taguette/migrations/versions/d86c45d7aa8a_add_document_direction.py +127 -0
  91. taguette/migrations/versions/db5e31a0233d_remove_blank_highlights.py +88 -0
  92. taguette/migrations/versions/dd37d9027a24_unique_tags.py +68 -0
  93. taguette/migrations/versions/de678cef2a94_add_project_id_to_collections_and_.py +41 -0
  94. taguette/migrations/versions/e459991d960e_add_table_edugainidps.py +39 -0
  95. taguette/migrations/versions/e4cf92942271_add_index.py +29 -0
  96. taguette/migrations/versions/e4e090a5b511_remove_commands_document_id_fk.py +38 -0
  97. taguette/migrations/versions/e9a9f1c77971_add_favorite_column_to_document.py +32 -0
  98. taguette/migrations/versions/ecb4065de575_don_t_use_server_default_now_timezone_.py +44 -0
  99. taguette/migrations/versions/fbdc29566f62_edugain_fix_field_type.py +42 -0
  100. taguette/migrations/versions/fdc6eb54b034_add_parent_id_column_to_tags_table.py +31 -0
  101. taguette/static/README.md +34 -0
  102. taguette/static/alt_templates/.gitkeep +0 -0
  103. taguette/static/assets/test/logos/monsters-university-logo.png +0 -0
  104. taguette/static/css/custom.css +1696 -0
  105. taguette/static/css/fa-all.min.css +5 -0
  106. taguette/static/css/themed.css +3 -0
  107. taguette/static/css/themed.css.map +25 -0
  108. taguette/static/css/vendor/bootstrap-treeview.min.css +1 -0
  109. taguette/static/css/vendor/bootstrap.min.css +6 -0
  110. taguette/static/css/vendor/font/context-menu-icons.eot +0 -0
  111. taguette/static/css/vendor/font/context-menu-icons.ttf +0 -0
  112. taguette/static/css/vendor/font/context-menu-icons.woff +0 -0
  113. taguette/static/css/vendor/font/context-menu-icons.woff2 +0 -0
  114. taguette/static/css/vendor/hierarchy-select.min.css +1 -0
  115. taguette/static/css/vendor/introjs.min.css +2 -0
  116. taguette/static/css/vendor/jquery.contextMenu.min.css +16 -0
  117. taguette/static/favicon.ico +0 -0
  118. taguette/static/js/codebook-viz.js +428 -0
  119. taguette/static/js/collection/context-menu.js +180 -0
  120. taguette/static/js/collection/hierarchy-select.js +71 -0
  121. taguette/static/js/collection/manager.js +217 -0
  122. taguette/static/js/document/context-menu.js +92 -0
  123. taguette/static/js/document/custom-select.js +82 -0
  124. taguette/static/js/document/manager.js +943 -0
  125. taguette/static/js/document/treeview.js +593 -0
  126. taguette/static/js/highlight/README.md +18 -0
  127. taguette/static/js/highlight/crud.js +119 -0
  128. taguette/static/js/highlight/extracts.js +497 -0
  129. taguette/static/js/highlight/loader.js +109 -0
  130. taguette/static/js/highlight/renderer.js +405 -0
  131. taguette/static/js/login.js +15 -0
  132. taguette/static/js/permissions.js +153 -0
  133. taguette/static/js/polling-event.js +145 -0
  134. taguette/static/js/project/intro.js +524 -0
  135. taguette/static/js/project/manager.js +0 -0
  136. taguette/static/js/project/search.js +124 -0
  137. taguette/static/js/tag/context-menu.js +133 -0
  138. taguette/static/js/tag/explore-codes.js +400 -0
  139. taguette/static/js/tag/hierarchy-select.js +69 -0
  140. taguette/static/js/tag/manager.js +545 -0
  141. taguette/static/js/tag/treeview.js +613 -0
  142. taguette/static/js/taguette.js +1790 -0
  143. taguette/static/js/utilities.js +714 -0
  144. taguette/static/js/vendor/bootstrap-treeview-custum.js +421 -0
  145. taguette/static/js/vendor/bootstrap-treeview-custum.min.js +1 -0
  146. taguette/static/js/vendor/bootstrap-treeview-default.min.js +1 -0
  147. taguette/static/js/vendor/bootstrap.bundle.min.js +7 -0
  148. taguette/static/js/vendor/bootstrap.bundle.min.js.map +1 -0
  149. taguette/static/js/vendor/hierarchy-select.min.js +1 -0
  150. taguette/static/js/vendor/introjs.min.js +17 -0
  151. taguette/static/js/vendor/jquery-3.7.1.js +2 -0
  152. taguette/static/js/vendor/jquery.contextMenu.min.js +2 -0
  153. taguette/static/libreqda.ico +0 -0
  154. taguette/static/logo-h.png +0 -0
  155. taguette/static/logo.png +0 -0
  156. taguette/static/logo_blanc.png +0 -0
  157. taguette/static/missing.png +0 -0
  158. taguette/static/robots.txt +2 -0
  159. taguette/static/scss/components/_badge.scss +42 -0
  160. taguette/static/scss/components/_button.scss +169 -0
  161. taguette/static/scss/components/_checkbox.scss +40 -0
  162. taguette/static/scss/components/_context_menu.scss +28 -0
  163. taguette/static/scss/components/_custom_select.scss +150 -0
  164. taguette/static/scss/components/_dropdown.scss +43 -0
  165. taguette/static/scss/components/_form.scss +54 -0
  166. taguette/static/scss/components/_modal.scss +57 -0
  167. taguette/static/scss/components/_project_card.scss +163 -0
  168. taguette/static/scss/components/_tag.scss +308 -0
  169. taguette/static/scss/components/_treeview.scss +76 -0
  170. taguette/static/scss/custom.scss +809 -0
  171. taguette/static/scss/layout/_credits.scss +62 -0
  172. taguette/static/scss/layout/_footer.scss +6 -0
  173. taguette/static/scss/layout/_navigation.scss +165 -0
  174. taguette/static/scss/layout/_sidebar.scss +37 -0
  175. taguette/static/scss/themes/default/_variables.scss +103 -0
  176. taguette/static/scss/themes/default/theme.css +3 -0
  177. taguette/static/scss/themes/default/theme.css.map +26 -0
  178. taguette/static/scss/themes/default/theme.scss +4 -0
  179. taguette/static/webfonts/BadScript-OFL.txt +94 -0
  180. taguette/static/webfonts/BadScript.ttf +0 -0
  181. taguette/static/webfonts/Montserrat-Light.ttf +0 -0
  182. taguette/static/webfonts/Montserrat-OFL.txt +93 -0
  183. taguette/static/webfonts/fa-brands-400.eot +0 -0
  184. taguette/static/webfonts/fa-brands-400.svg +1184 -0
  185. taguette/static/webfonts/fa-brands-400.ttf +0 -0
  186. taguette/static/webfonts/fa-brands-400.woff +0 -0
  187. taguette/static/webfonts/fa-brands-400.woff2 +0 -0
  188. taguette/static/webfonts/fa-regular-400.eot +0 -0
  189. taguette/static/webfonts/fa-regular-400.svg +467 -0
  190. taguette/static/webfonts/fa-regular-400.ttf +0 -0
  191. taguette/static/webfonts/fa-regular-400.woff +0 -0
  192. taguette/static/webfonts/fa-regular-400.woff2 +0 -0
  193. taguette/static/webfonts/fa-solid-900.eot +0 -0
  194. taguette/static/webfonts/fa-solid-900.svg +2618 -0
  195. taguette/static/webfonts/fa-solid-900.ttf +0 -0
  196. taguette/static/webfonts/fa-solid-900.woff +0 -0
  197. taguette/static/webfonts/fa-solid-900.woff2 +0 -0
  198. taguette/templates/alt_templates/.gitkeep +0 -0
  199. taguette/templates/archived_projects.html +50 -0
  200. taguette/templates/auth/account.html +74 -0
  201. taguette/templates/auth/cilogon.login.html +89 -0
  202. taguette/templates/auth/email_reset_password.html +5 -0
  203. taguette/templates/auth/external_account.html +60 -0
  204. taguette/templates/auth/login.html +144 -0
  205. taguette/templates/auth/reset_password.html +27 -0
  206. taguette/templates/auth/token_needed.html +7 -0
  207. taguette/templates/base.html +209 -0
  208. taguette/templates/bug_report.html +158 -0
  209. taguette/templates/cookies_prompt.html +15 -0
  210. taguette/templates/downloads.html +127 -0
  211. taguette/templates/email_bug_report.txt +9 -0
  212. taguette/templates/email_reset_password.txt +5 -0
  213. taguette/templates/error.html +8 -0
  214. taguette/templates/export_codebook.html +20 -0
  215. taguette/templates/export_document.html +20 -0
  216. taguette/templates/export_highlights.html +47 -0
  217. taguette/templates/index.html +347 -0
  218. taguette/templates/maintenance.html +8 -0
  219. taguette/templates/messages.js +74 -0
  220. taguette/templates/modal/change_document.html +68 -0
  221. taguette/templates/modal/create_collection.html +100 -0
  222. taguette/templates/modal/create_document.html +84 -0
  223. taguette/templates/modal/create_highlight.html +38 -0
  224. taguette/templates/modal/create_tag.html +73 -0
  225. taguette/templates/modal/explore_codes.html +58 -0
  226. taguette/templates/modal/manage_collaborators.html +44 -0
  227. taguette/templates/modal/merge_tag.html +49 -0
  228. taguette/templates/new_password.html +32 -0
  229. taguette/templates/partials/alert.html +16 -0
  230. taguette/templates/partials/codebook_viz_modal.html +34 -0
  231. taguette/templates/partials/confirm.html +17 -0
  232. taguette/templates/partials/document_toolbar.html +37 -0
  233. taguette/templates/partials/hlinfo.html +13 -0
  234. taguette/templates/partials/project_card.html +44 -0
  235. taguette/templates/partials/project_card_archived.html +41 -0
  236. taguette/templates/partials/project_sidebar.html +99 -0
  237. taguette/templates/partials/spinner.html +11 -0
  238. taguette/templates/permissions.html +54 -0
  239. taguette/templates/project.html +288 -0
  240. taguette/templates/project_delete.html +17 -0
  241. taguette/templates/project_import.html +122 -0
  242. taguette/templates/project_import_codebook.html +34 -0
  243. taguette/templates/project_import_codebook_confirm.html +49 -0
  244. taguette/templates/project_new.html +31 -0
  245. taguette/templates/tos.html +8 -0
  246. taguette/templates/trans.js +7 -0
  247. taguette/templates/welcome.html +15 -0
  248. taguette/utils.py +177 -0
  249. taguette/validate.py +141 -0
  250. taguette/version_check.py +102 -0
  251. taguette/web/__init__.py +252 -0
  252. taguette/web/api.py +1835 -0
  253. taguette/web/base.py +864 -0
  254. taguette/web/export.py +338 -0
  255. taguette/web/views.py +1618 -0
@@ -0,0 +1,84 @@
1
+ <!-- Add document modal -->
2
+ <div class="modal fade" id="document-add-modal" tabindex="-1" role="dialog" data-bs-backdrop="static">
3
+ <div class="modal-dialog modal-dialog-centered modal-lg" role="document" style="max-width: 1100px; width: 95vw;">
4
+ <form id="document-add-form" class="modal-content">
5
+ <div class="modal-header">
6
+ <h5 class="modal-title">{% trans "modal window title" %}Add a document{% endtrans %}</h5>
7
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
8
+ </div>
9
+ <div class="modal-body">
10
+ <div class="container">
11
+ <div class="row">
12
+ <div class="col form-group">
13
+ <input type="file" class="form-control" id="document-add-file" name="document-add-file"
14
+ required multiple />
15
+ </div>
16
+ </div>
17
+ <div class="row">
18
+ <div class="col-12 form-group">
19
+ <div class="d-flex align-items-center gap-3 flex-wrap">
20
+ <div class="form-check form-check-inline">
21
+ <input class="form-check-input" type="radio" name="document-add-direction"
22
+ id="document-add-direction-ltr" data-test-id="document-add-direction-ltr-input"
23
+ value="LEFT_TO_RIGHT" checked />
24
+ <label class="form-check-label" for="document-add-direction-ltr">{% trans %}Left to
25
+ right{% endtrans %}</label>
26
+ </div>
27
+ <div class="form-check form-check-inline">
28
+ <input class="form-check-input" type="radio" name="document-add-direction"
29
+ id="document-add-direction-rtl" data-test-id="document-add-direction-rtl-input"
30
+ value="RIGHT_TO_LEFT" />
31
+ <label class="form-check-label" for="document-add-direction-rtl">{% trans %}Right to
32
+ left{% endtrans %}</label>
33
+ </div>
34
+ <textarea class="form-control" id="document-add-description" name="document-add-description"
35
+ placeholder="{% trans " document add form placeholder" %}Description (will be applied to every documents){% endtrans %}"></textarea>
36
+ </div>
37
+ </div>
38
+ </div>
39
+
40
+ <!-- Quick actions section -->
41
+ <div class="row" id="bulk-actions-container" style="display: none;">
42
+ <div class="col form-group">
43
+ <div class="d-flex gap-2 align-items-center flex-wrap justify-content-end">
44
+ <div class="d-flex align-items-center gap-2">
45
+ <label class="form-label mb-0">{% trans %}Assign all to:{% endtrans %}</label>
46
+ <select class="form-select form-select-sm" id="assign-all-collection-select"
47
+ style="width: 200px;">
48
+ <option value="" selected>{% trans %}No collection (root){% endtrans %}</option>
49
+ </select>
50
+ </div>
51
+ <button type="button" class="btn btn-outline-primary btn-sm"
52
+ id="create-new-collection-for-all">
53
+ {% trans %}Create new collection for all{% endtrans %}
54
+ </button>
55
+ <button type="button" class="btn btn-outline-danger btn-sm"
56
+ id="reset-all-collections"
57
+ title="{% trans %}Reset all to no collection{% endtrans %}">
58
+ <i class="fas fa-undo me-1"></i>{% trans %}Reset{% endtrans %}
59
+ </button>
60
+ </div>
61
+ </div>
62
+ </div>
63
+
64
+ <!-- Selected documents list section -->
65
+ <div class="row" id="document-files-list-container" style="display: none;">
66
+ <div class="col form-group">
67
+ <label class="form-label">{% trans %}Selected documents and collections{% endtrans
68
+ %}</label>
69
+ <div id="document-files-list" class="border rounded p-3"
70
+ style="max-height: 45vh; overflow-y: auto;">
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ <div class="modal-footer">
77
+ <button type="submit" class="btn btn-primary" data-test-id="import-document-button">{% trans "submit
78
+ button" %}Import{% endtrans %}</button>
79
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans %}Cancel{% endtrans
80
+ %}</button>
81
+ </div>
82
+ </form>
83
+ </div>
84
+ </div>
@@ -0,0 +1,38 @@
1
+ <div class="modal fade" id="highlight-add-modal" tabindex="-1" role="dialog">
2
+ <div class="modal-dialog modal-dialog-centered" role="document">
3
+ <form id="highlight-add-form" class="modal-content">
4
+ <div class="modal-header">
5
+ <h5 class="modal-title">{% trans "modal window title" %}Highlight{% endtrans %}</h5>
6
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
7
+ </div>
8
+ <div class="modal-body">
9
+ <input type="hidden" id="highlight-add-id"/>
10
+ <input type="hidden" id="highlight-add-start"/>
11
+ <input type="hidden" id="highlight-add-end"/>
12
+ <p id="highlight-selection-preview" class="text-muted fst-italic small mx-3 mt-2 mb-2" style="display:none; white-space: pre-wrap;"></p>
13
+ <div class="container mb-1 mt-1">
14
+ <div class="form-group">
15
+ <input type="text" class="form-control" id="input-search-tags-highlight" placeholder="{% trans "tag change form placeholder" %}Search for a code{% endtrans %}">
16
+ </div>
17
+ </div>
18
+ <div id="highlight-selected-tag-area" class="tag-selection-box mt-3 mb-3">
19
+ </div>
20
+ <div class="container mb-3 mt-3">
21
+ <div id="treeview-tags-highlight"></div>
22
+ </div>
23
+ <div class="alert alert-info mt-2 d-none" role="alert" id="alert-tag-highlight-treeview">
24
+ </div>
25
+ <div class="container mb-3 mt-3" id="container-create-tag-highlight">
26
+ <a href="javascript:createTag(true)" data-test-id="create-tag-from-highlight-link">{% trans %}Create a code{% endtrans
27
+ %}</a>
28
+ </div>
29
+ </div>
30
+ <div class="modal-footer">
31
+ <button type="submit" class="btn btn-primary" data-test-id="save-highlight-button">{% trans %}Save & Close{% endtrans %}</button>
32
+ <button type="button" class="btn btn-danger" data-bs-dismiss="modal" id="highlight-delete">{% trans
33
+ %}Delete highlight{% endtrans %}
34
+ </button>
35
+ </div>
36
+ </form>
37
+ </div>
38
+ </div>
@@ -0,0 +1,73 @@
1
+ <!-- Add/change tag modal -->
2
+ <div class="modal fade" id="tag-add-modal" tabindex="-1" role="dialog" data-bs-backdrop="static">
3
+ <div class="modal-dialog modal-dialog-centered" role="document">
4
+ <form id="tag-add-form" class="modal-content">
5
+ <div class="modal-header">
6
+ <h5 id="tag-add-label-new" class="modal-title">{% trans "modal window title" %}New tag{% endtrans
7
+ %}</h5>
8
+ <h5 id="tag-add-label-change" class="modal-title" style="display: none;">{% trans "modal window title"
9
+ %}Change code{% endtrans %}</h5>
10
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
11
+ </div>
12
+ <div class="modal-body.no-scroll">
13
+ <input type="hidden" id="tag-add-id" />
14
+ <input type="hidden" id="tag-highlight-add-start" />
15
+ <input type="hidden" id="tag-highlight-add-end" />
16
+ <p id="tag-selection-preview" class="text-muted fst-italic small mx-3 mt-2 mb-2" style="display:none; white-space: pre-wrap;"></p>
17
+ <div class="container mb-3">
18
+ <div class="form-group">
19
+ <label for="tag-hierarchy-select" class="form-label">{% trans "tag change form placeholder"
20
+ %}Parent code{% endtrans %}</label>
21
+ <div class='dropdown hierarchy-select' id='tag-hierarchy-select'>
22
+ <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButtonTag"
23
+ data-bs-toggle="dropdown" aria-expanded="false"></button>
24
+ <ul class="dropdown-menu" aria-labelledby="dropdownMenuButtonTag">
25
+ <div class="hs-searchbox">
26
+ <input type="text" class="form-control" autocomplete="off">
27
+ </div>
28
+ <div class='hs-menu-inner'>
29
+ </div>
30
+ </ul>
31
+ <input class="d-none" id="tag-add-parent-id" name="tag-add-parent-id" readonly="readonly"
32
+ aria-hidden="true" type="text" />
33
+ </div>
34
+ </div>
35
+ <div class="form-group">
36
+ <label for="tag-add-color" class="form-label">{% trans "tag change form placeholder" %}Name{%
37
+ endtrans %}</label>
38
+ <input type="text" class="form-control" id="tag-add-path" name="tag-add-path"
39
+ placeholder="{% trans " tag change form placeholder" %}Name{% endtrans %}" required />
40
+ </div>
41
+ <div class="form-group">
42
+ <label for="tag-add-color" class="form-label">{% trans "tag change form placeholder" %}Color{%
43
+ endtrans %}</label>
44
+ <input type="color" class="form-control" id="tag-add-color" name="tag-add-color" required />
45
+ </div>
46
+ <div class="form-group">
47
+ <label for="tag-add-color" class="form-label">{% trans " tag change form placeholder"
48
+ %}Description{% endtrans
49
+ %}</label>
50
+ <textarea class="form-control" id="tag-add-description" name="tag-add-description"
51
+ placeholder="{% trans " tag change form placeholder" %}Description{% endtrans
52
+ %}"></textarea>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ <div class="modal-footer">
57
+ <button type="button" class="btn btn-info" data-bs-dismiss="modal" id="tag-add-merge"
58
+ data-test-id="show-merge-tag-modal-button">{% trans "button
59
+ in tag change modal window" %}Merge...{% endtrans %}
60
+ </button>
61
+ <button type="submit" class="btn btn-primary" data-test-id="save-tag-button">{% trans %}Save & Close{%
62
+ endtrans %}</button>
63
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" data-test-id="tag-add-cancel"
64
+ id="tag-add-cancel">{% trans
65
+ %}Cancel{% endtrans %}
66
+ </button>
67
+ <button type="button" class="btn btn-danger" data-bs-dismiss="modal" id="tag-add-delete">{% trans
68
+ %}Delete tag{% endtrans %}
69
+ </button>
70
+ </div>
71
+ </form>
72
+ </div>
73
+ </div>
@@ -0,0 +1,58 @@
1
+ <!-- Explore codes modal: visual AND/OR/NOT query builder -->
2
+ <div class="modal fade" id="explore-codes-modal" tabindex="-1" role="dialog" data-bs-backdrop="static">
3
+ <div class="modal-dialog modal-dialog-centered modal-lg" role="document">
4
+ <div class="modal-content">
5
+ <div class="modal-header">
6
+ <h5 class="modal-title">{% trans "modal window title" %}Explore codes{% endtrans %}</h5>
7
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
8
+ </div>
9
+ <div class="modal-body">
10
+ <p class="text-muted small mb-2">{% trans %}Click on a box below to pick codes.{% endtrans %}</p>
11
+
12
+ <div id="explore-zone-can-box" class="tag-selection-box explore-zone-box mb-2"
13
+ role="button" tabindex="0" title="{% trans %}Click to pick codes for this zone{% endtrans %}"
14
+ onclick="toggleExploreZone('can')" onkeydown="handleZoneBoxKeydown(event, 'can')">
15
+ <div class="zone-label">
16
+ <span>{% trans %}Can use{% endtrans %} <small class="text-muted">({% trans "explore zone hint" %}any of these{% endtrans %})</small></span>
17
+ </div>
18
+ <div class="zone-chips" id="explore-zone-can-chips"></div>
19
+ </div>
20
+ <div id="explore-zone-must-box" class="tag-selection-box explore-zone-box mb-2"
21
+ role="button" tabindex="0" title="{% trans %}Click to pick codes for this zone{% endtrans %}"
22
+ onclick="toggleExploreZone('must')" onkeydown="handleZoneBoxKeydown(event, 'must')">
23
+ <div class="zone-label">
24
+ <span>{% trans %}Must use{% endtrans %} <small class="text-muted">({% trans "explore zone hint" %}all of these{% endtrans %})</small></span>
25
+ </div>
26
+ <div class="zone-chips" id="explore-zone-must-chips"></div>
27
+ </div>
28
+ <div id="explore-zone-exclude-box" class="tag-selection-box explore-zone-box mb-3"
29
+ role="button" tabindex="0" title="{% trans %}Click to pick codes for this zone{% endtrans %}"
30
+ onclick="toggleExploreZone('exclude')" onkeydown="handleZoneBoxKeydown(event, 'exclude')">
31
+ <div class="zone-label">
32
+ <span>{% trans %}Exclude{% endtrans %} <small class="text-muted">({% trans "explore zone hint" %}none of these{% endtrans %})</small></span>
33
+ </div>
34
+ <div class="zone-chips" id="explore-zone-exclude-chips"></div>
35
+ </div>
36
+
37
+ <div id="explore-code-picker" class="d-none">
38
+ <div class="container mb-1 mt-1">
39
+ <div class="form-group">
40
+ <input type="text" class="form-control" id="input-search-tags-explore" autocomplete="off"
41
+ placeholder="{% trans "tag change form placeholder" %}Search for a code{% endtrans %}">
42
+ </div>
43
+ </div>
44
+ <div class="container mb-3 mt-3">
45
+ <div id="treeview-tags-explore"></div>
46
+ </div>
47
+ <div class="alert alert-info mt-2 d-none" role="alert" id="alert-tag-explore-treeview">
48
+ </div>
49
+ </div>
50
+ </div>
51
+ <div class="modal-footer">
52
+ <button type="button" class="btn btn-primary" id="explore-codes-apply" disabled
53
+ data-test-id="explore-codes-apply-button">{% trans %}Search{% endtrans %}</button>
54
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans %}Cancel{% endtrans %}</button>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </div>
@@ -0,0 +1,44 @@
1
+ <!-- Manage collaborators modal -->
2
+ <div class="modal fade" id="members-modal" tabindex="-1" role="dialog" data-bs-backdrop="static">
3
+ <div class="modal-dialog modal-dialog-centered modal-lg" role="document">
4
+ <div class="modal-content">
5
+ <div class="modal-header">
6
+ <h5 class="modal-title">{% trans %}Manage Collaborators{% endtrans %}</h5>
7
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
8
+ </div>
9
+ <div class="modal-body">
10
+ <div class="container">
11
+ <h5>{% trans "modal window title" %}Add Collaborators{% endtrans %}</h5>
12
+ <form id="members-add">
13
+ <fieldset id="members-add-fields">
14
+ <div class="row">
15
+ <div class="col-md-4 form-group">
16
+ {% if is_external_login %}
17
+ <input type="text" class="form-control" id="member-add-name" placeholder="{% trans "form label for email address" %}Email{% endtrans %}" required />
18
+ {% else %}
19
+ <input type="text" class="form-control" id="member-add-name" placeholder="{% trans "project members form placeholder" %}Username{% endtrans %}" required />
20
+ {% endif %}
21
+ </div>
22
+ <div class="col-md-4 form-group">
23
+ <select id="member-add-privileges" class="form-control">
24
+ <option value="ADMIN">{% trans "permission level" %}Full permissions{% endtrans %}</option>
25
+ <option value="MANAGE_DOCS">{% trans "permission level" %}Can't change collaborators / delete project{% endtrans %}</option>
26
+ <option value="TAG" selected>{% trans "permission level" %}View & make changes{% endtrans %}</option>
27
+ <option value="READ">{% trans "permission level" %}View only{% endtrans %}</option>
28
+ </select>
29
+ </div>
30
+ <button type="submit" class="btn btn-info col-md-4 form-group">{% trans "form submit button" %}Add to project{% endtrans %}</button>
31
+ </div>
32
+ </fieldset>
33
+ </form>
34
+ <h5>{% trans %}Current Collaborators{% endtrans %}</h5>
35
+ <form id="members-current">
36
+ </form>
37
+ </div>
38
+ </div>
39
+ <div class="modal-footer">
40
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans %}Close{% endtrans %}</button>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
@@ -0,0 +1,49 @@
1
+ <!-- Merge tags modal -->
2
+ <div class="modal fade" id="tag-merge-modal" tabindex="-1" role="dialog" data-bs-backdrop="static">
3
+ <div class="modal-dialog modal-dialog-centered" role="document">
4
+ <form id="tag-merge-form" class="modal-content">
5
+ <div class="modal-header">
6
+ <h5 class="modal-title">{% trans "merge modal" %}Merge codes{% endtrans %}</h5>
7
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
8
+ </div>
9
+ <div class="modal-body.no-scroll">
10
+ <div class="container mt-3">
11
+ <div class="row">
12
+ <div class="col-md-5 form-group">
13
+ <input type="hidden" id="tag-merge-src-id" />
14
+ <input type="text" class="form-control" id="tag-merge-src-name" disabled />
15
+ </div>
16
+ <div class="col-md-2 form-group">
17
+ <label for="tag-hierarchy-merge-select">{% trans %}into{% endtrans %}</label>
18
+ </div>
19
+ <div class="col-md-5 form-group">
20
+ <div class='dropdown hierarchy-select' id='tag-hierarchy-merge-select'>
21
+ <button class="btn btn-secondary dropdown-toggle" type="button"
22
+ id="dropdownMenuButtonTagMerge" data-bs-toggle="dropdown"
23
+ aria-expanded="false"></button>
24
+ <ul class="dropdown-menu" aria-labelledby="dropdownMenuButtonTagMerge">
25
+ <div class="hs-searchbox">
26
+ <input type="text" class="form-control" autocomplete="off">
27
+ </div>
28
+ <div class='hs-menu-inner' data-test-id="hs-menu-inner-tag-merge">
29
+ </div>
30
+ </ul>
31
+ <input class="d-none" id="tag-merge-dest" name="tag-merge-dest" readonly="readonly"
32
+ aria-hidden="true" type="text" />
33
+ </div>
34
+ </div>
35
+ </div>
36
+ <div id="tag-merge-warning" class="alert alert-warning mt-3 d-none">
37
+ <i class="fas fa-exclamation-triangle"></i>
38
+ <span id="tag-merge-warning-text"></span>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ <div class="modal-footer">
43
+ <button type="submit" class="btn btn-danger" data-test-id="merge-tag-button">Merge codes</button>
44
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"
45
+ data-test-id="cancel-merge-tag-button">Cancel</button>
46
+ </div>
47
+ </form>
48
+ </div>
49
+ </div>
@@ -0,0 +1,32 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{% trans "page title" %}Reset password{% endtrans %} | LibreQDA{% endblock %}
4
+
5
+ {% block contents %}
6
+ {% if error %}
7
+ <div class="alert alert-danger">{{ error }}</div>
8
+ {% endif %}
9
+
10
+ <p>{% trans %}Please enter a new password below:{% endtrans %}</p>
11
+ <form action="{{ reverse_url('new_password') }}" method="post">
12
+ {{ xsrf_form_html() }}
13
+ <input type="hidden" name="reset_token" value="{{ reset_token }}" />
14
+ <div class="form-group row">
15
+ <label for="password1" class="col-sm-2 col-form-label">{% trans "form label" %}Password{% endtrans %}</label>
16
+ <div class="col-sm-8">
17
+ <input type="password" class="form-control" id="password1" name="password1" required />
18
+ </div>
19
+ </div>
20
+ <div class="form-group row">
21
+ <label for="password2" class="col-sm-2 col-form-label">{% trans "form label" %}Confirm password{% endtrans %}</label>
22
+ <div class="col-sm-8">
23
+ <input type="password" class="form-control" id="password2" name="password2" required />
24
+ </div>
25
+ </div>
26
+ <div class="form-group row">
27
+ <div class="col-sm-10 offset-sm-2">
28
+ <button type="submit" class="btn btn-primary">{% trans "form submit button" %}Set new password{% endtrans %}</button>
29
+ </div>
30
+ </div>
31
+ </form>
32
+ {% endblock %}
@@ -0,0 +1,16 @@
1
+ <!-- alert.html -->
2
+ <div class="modal fade" id="alertModal" tabindex="-1" aria-labelledby="alertModalLabel" data-bs-backdrop="static" aria-hidden="true">
3
+ <div class="modal-dialog">
4
+ <div class="modal-content">
5
+ <div class="modal-header">
6
+ <h5 class="modal-title" id="alertModalLabel"></h5>
7
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
8
+ </div>
9
+ <div class="modal-body" id="alertMessage">
10
+ </div>
11
+ <div class="modal-footer">
12
+ <button type="button" class="btn btn-primary" data-bs-dismiss="modal">Fermer</button>
13
+ </div>
14
+ </div>
15
+ </div>
16
+ </div>
@@ -0,0 +1,34 @@
1
+ <div class="modal fade" id="codebook-viz-modal" tabindex="-1"
2
+ aria-labelledby="codebook-viz-modal-label" aria-hidden="true">
3
+ <div class="modal-dialog modal-xl" style="max-width: 95vw;">
4
+ <div class="modal-content">
5
+ <div class="modal-header">
6
+ <h5 class="modal-title" id="codebook-viz-modal-label">{% trans %}Codebook visualization{% endtrans %}</h5>
7
+ <button type="button" class="btn-close" data-bs-dismiss="modal"
8
+ aria-label="{% trans %}Close{% endtrans %}"></button>
9
+ </div>
10
+ <div class="modal-body p-0" style="overflow: auto; height: 70vh;">
11
+ <div id="codebook-viz-container" style="width: fit-content; min-width: 100%;"></div>
12
+ </div>
13
+ <div class="modal-footer">
14
+ <div class="form-check mb-0 me-2">
15
+ <input class="form-check-input" type="checkbox" id="codebook-viz-transparent">
16
+ <label class="form-check-label" for="codebook-viz-transparent">
17
+ {% trans %}Transparent background{% endtrans %}
18
+ </label>
19
+ </div>
20
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
21
+ {% trans %}Close{% endtrans %}
22
+ </button>
23
+ <button type="button" class="btn btn-outline-primary" id="codebook-viz-export-png"
24
+ data-bs-dismiss="modal">
25
+ {% trans %}Export as PNG{% endtrans %}
26
+ </button>
27
+ <button type="button" class="btn btn-primary" id="codebook-viz-export-svg"
28
+ data-bs-dismiss="modal">
29
+ {% trans %}Export as SVG{% endtrans %}
30
+ </button>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ </div>
@@ -0,0 +1,17 @@
1
+ <!-- confirm.html -->
2
+ <div class="modal fade" id="confirmModal" tabindex="-1" aria-labelledby="confirmModal" aria-hidden="true" data-bs-backdrop="static">
3
+ <div class="modal-dialog">
4
+ <div class="modal-content">
5
+ <div class="modal-header">
6
+ <h5 class="modal-title" id="confirmModalLabel">{% trans "confirm modal" %}Confirm{% endtrans %}</h5>
7
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
8
+ </div>
9
+ <div class="modal-body" id="confirmMessage">
10
+ </div>
11
+ <div class="modal-footer">
12
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "confirm modal" %}Cancel{% endtrans %}</button>
13
+ <button type="button" class="btn btn-primary" id="confirmButton">{% trans "confirm modal confirm" %}Confirm{% endtrans %}</button>
14
+ </div>
15
+ </div>
16
+ </div>
17
+ </div>
@@ -0,0 +1,37 @@
1
+ <div class="toolbar-container py-3 px-2 d-flex align-items-center gap-3">
2
+
3
+ <div class="d-flex align-items-center gap-2 flex-grow-1" style="min-width: 0;">
4
+ <span class="text-label text-nowrap">{% trans %}Bookmarks{% endtrans %}</span>
5
+ <div class="custom-select rounded-5 flex-grow-1" style="width: auto; min-width: 0;">
6
+ <button class="select-button" data-test-id="select-document-button" role="combobox"
7
+ aria-labelledby="select button" aria-haspopup="listbox" aria-expanded="false"
8
+ aria-controls="select-dropdown">
9
+ <span class="selected-value">{% trans %}Left-click a document to bookmark it{% endtrans %}</span>
10
+ <span class="arrow"></span>
11
+ </button>
12
+ <ul class="select-dropdown" role="listbox" id="select-dropdown"></ul>
13
+ </div>
14
+ </div>
15
+
16
+ <div class="toolbar-action-buttons d-flex" style="flex-shrink: 0;">
17
+ <!-- Bouton Export -->
18
+ <div class="document-export-button dropdown">
19
+ <button class="btn btn-outline-primary dropdown-toggle d-flex align-items-center gap-2"
20
+ title="{% trans %}Download this view{% endtrans %}"
21
+ type="button" id="document-dropdown-export" data-test-id="document-dropdown-export"
22
+ data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
23
+ <i class="fas fa-download"></i>
24
+ <span>{% trans %}Download this view{% endtrans %}</span>
25
+ </button>
26
+ <div id='document-export-dropdown-menu' class="dropdown-menu" aria-labelledby="document-dropdown-export">
27
+ <a href="#" class="dropdown-item" data-extension="html">{% trans %}HTML{% endtrans %}</a>
28
+ <a href="#" class="dropdown-item" data-extension="docx">{% trans %}DOCX{% endtrans %}</a>
29
+ <a href="#" class="dropdown-item" data-extension="pdf">{% trans %}PDF{% endtrans %}</a>
30
+ <a href="#" class="dropdown-item" data-extension="xlsx" data-document="false">{% trans %}Excel{%
31
+ endtrans %}</a>
32
+ <a href="#" class="dropdown-item" data-extension="csv" data-document="false">{% trans %}CSV{% endtrans
33
+ %}</a>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </div>
@@ -0,0 +1,13 @@
1
+ <!-- "New highlight" button shown after selecting text -->
2
+ <div id="hlinfo" style="display: none; text-align: center;">
3
+ <div class="mb-2" style="padding: 0 15px;">
4
+ <a class="btn btn-outline-primary" href="javascript:createTag(false,current_selection);" style="display: block; width: 100%; box-sizing: border-box;">
5
+ {% trans %}New code{% endtrans %}
6
+ </a>
7
+ </div>
8
+ <div style="padding: 0 15px;">
9
+ <a class="btn btn-outline-secondary" href="javascript:createHighlight(current_selection);" style="display: block; width: 100%; box-sizing: border-box;">
10
+ {% trans %}Existing code{% endtrans %}
11
+ </a>
12
+ </div>
13
+ </div>
@@ -0,0 +1,44 @@
1
+ <div class="col project" data-project-id="{{ project.id }}">
2
+ <div class="project-card">
3
+ <!-- Header with name and actions -->
4
+ <div class="d-flex justify-content-between align-items-start px-3 pt-3">
5
+ <a href="{{ reverse_url('project', project.id) }}{% if demo %}?demo=true{% endif %}" class="no-underline flex-grow-1">
6
+ <h3 class="name" title="{{ project.name }}">{{ project.name }}</h3>
7
+ </a>
8
+ <!-- Actions dropdown -->
9
+ <div class="dropdown project-actions">
10
+ <button class="btn btn-link btn-sm text-muted p-0" type="button" data-bs-toggle="dropdown" aria-expanded="false">
11
+ <i class="fas fa-ellipsis-v"></i>
12
+ </button>
13
+ <ul class="dropdown-menu dropdown-menu-end">
14
+ <li><a class="dropdown-item action-edit" href="#" data-project-id="{{ project.id }}" data-project-name="{{ project.name }}" data-project-description="{{ project.description }}"><i class="fas fa-edit me-2"></i>{% trans %}Edit{% endtrans %}</a></li>
15
+ <li><a class="dropdown-item" href="{{ reverse_url('export_project_sqlite', project.id) }}"><i class="fas fa-download me-2"></i>{% trans %}Export{% endtrans %}</a></li>
16
+ <li><a class="dropdown-item action-archive" href="#" data-project-id="{{ project.id }}"><i class="fas fa-archive me-2"></i>{% trans %}Archive{% endtrans %}</a></li>
17
+ <li><hr class="dropdown-divider"></li>
18
+ <li><a class="dropdown-item text-danger" href="{{ reverse_url('delete_project', project.id) }}"><i class="fas fa-trash me-2"></i>{% trans %}Delete{% endtrans %}</a></li>
19
+ </ul>
20
+ </div>
21
+ </div>
22
+ <!-- Card link -->
23
+ <a href="{{ reverse_url('project', project.id) }}{% if demo %}?demo=true{% endif %}" class="no-underline project-card-link d-flex flex-column flex-grow-1">
24
+ <div class="px-3 pt-2 description-container">
25
+ <p class="quote2 description-text">{{ project.description | truncate(100, true, '...') }}</p>
26
+ </div>
27
+ <hr class="custom-hr mt-auto">
28
+ <div class="d-flex justify-content-between px-3 pb-3 stats-container">
29
+ <div class="d-flex align-items-center" title="{% trans %}Documents{% endtrans %}">
30
+ <i class="fas fa-file-alt"></i>
31
+ <span class="quote2 mx-2">{{ project.document_count }}</span>
32
+ </div>
33
+ <div class="d-flex align-items-center" title="{% trans %}Codes{% endtrans %}">
34
+ <i class="fas fa-stream"></i>
35
+ <span class="quote2 mx-2">{{ project.tag_count }}</span>
36
+ </div>
37
+ <div class="d-flex align-items-center" title="{% trans %}Collaborators{% endtrans %}">
38
+ <i class="fas fa-users"></i>
39
+ <span class="quote2 mx-2">{{ project.member_count }}</span>
40
+ </div>
41
+ </div>
42
+ </a>
43
+ </div>
44
+ </div>
@@ -0,0 +1,41 @@
1
+ <div class="col project" data-project-id="{{ project.id }}">
2
+ <div class="project-card">
3
+ <!-- Header with name and actions -->
4
+ <div class="d-flex justify-content-between align-items-start px-3 pt-3">
5
+ <h3 class="name" title="{{ project.name }}">{{ project.name }}</h3>
6
+ <!-- Actions dropdown -->
7
+ <div class="dropdown project-actions">
8
+ <button class="btn btn-link btn-sm text-muted p-0" type="button" data-bs-toggle="dropdown" aria-expanded="false">
9
+ <i class="fas fa-ellipsis-v"></i>
10
+ </button>
11
+ <ul class="dropdown-menu dropdown-menu-end">
12
+ <li><a class="dropdown-item action-unarchive" href="#" data-project-id="{{ project.id }}"><i class="fas fa-box-open me-2"></i>{% trans %}Unarchive{% endtrans %}</a></li>
13
+ <li><a class="dropdown-item" href="{{ reverse_url('export_project_sqlite', project.id) }}"><i class="fas fa-download me-2"></i>{% trans %}Export{% endtrans %}</a></li>
14
+ <li><hr class="dropdown-divider"></li>
15
+ <li><a class="dropdown-item text-danger" href="{{ reverse_url('delete_project', project.id) }}"><i class="fas fa-trash me-2"></i>{% trans %}Delete{% endtrans %}</a></li>
16
+ </ul>
17
+ </div>
18
+ </div>
19
+ <!-- Card body (no link) -->
20
+ <div class="d-flex flex-column flex-grow-1">
21
+ <div class="px-3 pt-2 description-container">
22
+ <p class="quote2 description-text">{{ project.description | truncate(100, true, '...') }}</p>
23
+ </div>
24
+ <hr class="custom-hr mt-auto">
25
+ <div class="d-flex justify-content-between px-3 pb-3 stats-container">
26
+ <div class="d-flex align-items-center" title="{% trans %}Documents{% endtrans %}">
27
+ <i class="fas fa-file-alt"></i>
28
+ <span class="quote2 mx-2">{{ project.document_count }}</span>
29
+ </div>
30
+ <div class="d-flex align-items-center" title="{% trans %}Codes{% endtrans %}">
31
+ <i class="fas fa-stream"></i>
32
+ <span class="quote2 mx-2">{{ project.tag_count }}</span>
33
+ </div>
34
+ <div class="d-flex align-items-center" title="{% trans %}Collaborators{% endtrans %}">
35
+ <i class="fas fa-users"></i>
36
+ <span class="quote2 mx-2">{{ project.member_count }}</span>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ </div>