diff --git a/airlock/settings.py b/airlock/settings.py index 488460879..cebcd61de 100644 --- a/airlock/settings.py +++ b/airlock/settings.py @@ -91,7 +91,6 @@ def get_env_var(name): # requirements for assets library "django.contrib.humanize", "django_vite", - "slippers", "django_htmx", "django_extensions", ] @@ -150,8 +149,7 @@ def get_env_var(name): "airlock.nav.dev_users", ], "builtins": [ - "slippers.templatetags.slippers", # required for assets library - "airlock.templatetags.airlock_components", # required for airlock custom components + "airlock.templatetags.template_components", ], "debug": DEBUG, # required for template coverage }, @@ -383,9 +381,30 @@ class MissingVariableErrorFilter(logging.Filter): "_partials", ) + def _resolve_actual_template(self, exc_info): + # context.template_name is the top-level template, so {% include %}d + # templates can't be distinguished by that name. The render_context tracks + # the actual template currently being rendered via push_state — look it up + # via the exception traceback's frame locals. + if not exc_info: + return None + tb = exc_info[2] + while tb: + ctx = tb.tb_frame.f_locals.get("context") + render_ctx = getattr(ctx, "render_context", None) + template = getattr(render_ctx, "template", None) + origin = getattr(template, "origin", None) + name = getattr(origin, "template_name", None) + if name: + return name + tb = tb.tb_next + return None + def filter(self, record): # pragma: no cover if record.msg.startswith("Exception while resolving variable "): - template_name = record.args[1] + template_name = ( + self._resolve_actual_template(record.exc_info) or record.args[1] + ) if ( not template_name.startswith(self.ignored_prefixes) # This shows up when rendering Django's internal error pages diff --git a/airlock/templates/_components/clusterize-table.html b/airlock/templates/_components/clusterize-table.html index 30b20272b..8cf0e2694 100644 --- a/airlock/templates/_components/clusterize-table.html +++ b/airlock/templates/_components/clusterize-table.html @@ -172,7 +172,7 @@ {% endcomment %} - {{ children }} + {% body %} diff --git a/airlock/templates/_components/datatable.html b/airlock/templates/_components/datatable.html index fbd2f3daa..15b4134ab 100644 --- a/airlock/templates/_components/datatable.html +++ b/airlock/templates/_components/datatable.html @@ -14,7 +14,7 @@ {% endif %} {% attrs id data-column-filter=column_filter data-sortable=sortable data-searchable=searchable %} > - {{ children }} + {% body %} diff --git a/airlock/templates/_components/header/base.html b/airlock/templates/_components/header/base.html index dbd1021c6..1dcf68ae5 100644 --- a/airlock/templates/_components/header/base.html +++ b/airlock/templates/_components/header/base.html @@ -5,9 +5,7 @@

{% block content %}{% endblock %} - {% if children %} -
- {{ children }} -
+ {% body as body_content %}{% if body_content %} +
{{ body_content }}
{% endif %} diff --git a/airlock/templates/_components/header/request/header.html b/airlock/templates/_components/header/request/header.html index 94db32663..38ababc1b 100644 --- a/airlock/templates/_components/header/request/header.html +++ b/airlock/templates/_components/header/request/header.html @@ -1,7 +1,7 @@ {% extends "../base.html" %} {% block extra_title %} - {% pill variant="info" text=release_request.status.description %} + {% include "_components/pill/pill.html" with variant="info" text=release_request.status.description %} {% endblock %} {% block content %} @@ -37,7 +37,7 @@ User -
{% airlock_user user=release_request.author %}
+
{% include "_components/user.html" with user=release_request.author %}
diff --git a/airlock/templates/_components/list-group/list-group-item-dropdown.html b/airlock/templates/_components/list-group/list-group-item-dropdown.html index 497e429e7..c778715a7 100644 --- a/airlock/templates/_components/list-group/list-group-item-dropdown.html +++ b/airlock/templates/_components/list-group/list-group-item-dropdown.html @@ -1,8 +1,8 @@
  • {% if not disabled %} - - {{ children }} + + {% body %} {% if custom_button %} {{ custom_button }} diff --git a/airlock/templates/_components/list-group/list-group-rich-item.html b/airlock/templates/_components/list-group/list-group-rich-item.html index 9a60616ab..35fb9a613 100644 --- a/airlock/templates/_components/list-group/list-group-rich-item.html +++ b/airlock/templates/_components/list-group/list-group-rich-item.html @@ -50,22 +50,22 @@ {% if status_text %} {% if status_text == "APPROVED - FILES UPLOADING" or status_text == "RELEASED" %} - {% pill variant="success" text=status_text %} + {% include "_components/pill/pill.html" with variant="success" text=status_text %} {% elif status_text == "SUBMITTED" or status_text == "ONE REVIEW SUBMITTED" or status_text == "ALL REVIEWS SUBMITTED" %} - {% pill variant="primary" text=status_text %} + {% include "_components/pill/pill.html" with variant="primary" text=status_text %} {% elif status_text == "REJECTED" %} - {% pill variant="danger" text=status_text %} + {% include "_components/pill/pill.html" with variant="danger" text=status_text %} {% elif status_text == "RETURNED" %} - {% pill variant="warning" text=status_text %} + {% include "_components/pill/pill.html" with variant="warning" text=status_text %} {% elif status_text == "PENDING" %} - {% pill variant="info" text=status_text %} + {% include "_components/pill/pill.html" with variant="info" text=status_text %} {% elif status_text == "WITHDRAWN" %} - {% pill variant="danger-outline" text=status_text %} + {% include "_components/pill/pill.html" with variant="danger-outline" text=status_text %} {% endif %} {% elif custom_status %} {{ custom_status }} @@ -74,6 +74,6 @@ {% endif %}
    - {{ children }} + {% body %}
  • diff --git a/airlock/templates/_includes/header.html b/airlock/templates/_includes/header.html index 5699caf9c..484c4c27a 100644 --- a/airlock/templates/_includes/header.html +++ b/airlock/templates/_includes/header.html @@ -60,7 +60,7 @@ " data-testid="switch-user" > - {% airlock_user user=request.user %} + {% include "_components/user.html" with user=request.user %} diff --git a/airlock/templates/activity.html b/airlock/templates/activity.html index fb40e4ab6..003902123 100644 --- a/airlock/templates/activity.html +++ b/airlock/templates/activity.html @@ -1,9 +1,9 @@ {% load airlock %} {% load django_vite %} -{% #card title=title class="mt-5" %} +{% component "_components/card/card.html" with title=title class="mt-5" %} {% if activity %} - {% #datatable per_page="10" column_filter searchable sortable %} + {% component "_components/datatable.html" with per_page="10" column_filter=True searchable=True sortable=True %} @@ -44,7 +44,7 @@ {% for log in activity %} {{ log.created_at|date:'Y-m-d H:i' }} - {% airlock_user user=log.user %} + {% include "_components/user.html" with user=log.user %} {{ log.description }}
      @@ -57,12 +57,12 @@ {% endfor %} - {% /datatable %} + {% endcomponent %} {% else %} - {% #list_group %} - {% list_group_empty title="No activity" description="There has been no recent activity on this workspace" %} - {% /list_group %} + {% component "_components/list-group/list-group.html" %} + {% include "_components/list-group/list-group-empty.html" with title="No activity" description="There has been no recent activity on this workspace" %} + {% endcomponent %} {% endif %} -{% /card %} +{% endcomponent %} {% vite_asset "assets/src/scripts/datatable.js" %} diff --git a/airlock/templates/add_or_change_files.html b/airlock/templates/add_or_change_files.html index fe0d11485..526d4482b 100644 --- a/airlock/templates/add_or_change_files.html +++ b/airlock/templates/add_or_change_files.html @@ -5,46 +5,46 @@ div.filetype-radio div { display: inline; } div.filetype-radio label { display: inline; } -{% #modal id="addOrChangeRequestFile" custom_button=button autoshow=True %} - {% #card container=True title=modal_title %} +{% component "_components/modal/modal.html" with id="addOrChangeRequestFile" custom_button=button autoshow=True %} + {% component "_components/card/card.html" with container=True title=modal_title %}
      {% csrf_token %} {{ form.next_url }} {{ formset.management_form }} - {% #list_group %} + {% component "_components/list-group/list-group.html" %} {% for name, reason in files_ignored.items %} - {% #list_group_item %} + {% component "_components/list-group/list-group-item.html" %}
      {{ name }} {{ reason }}
      - {% /list_group_item %} + {% endcomponent %} {% endfor %} {% for formset_form in formset %} - {% #list_group_item %} + {% component "_components/list-group/list-group-item.html" %}
      {{ formset_form.file.value }} {{ formset_form.file }} - {% form_radios field=formset_form.filetype class="flex flex-row whitespace-nowrap gap-2" selected=formset_form.filetype.initial %} + {% include "_components/form/radio_list.html" with field=formset_form.filetype class="flex flex-row whitespace-nowrap gap-2" selected=formset_form.filetype.initial %}
      {% if formset_form.filetype.help_text %}
      {{ formset_form.filetype.help_text }}
      {% endif %}
      - {% /list_group_item %} + {% endcomponent %} {% endfor %} - {% /list_group %} + {% endcomponent %} {% if not no_valid_files %} - {% form_select class="w-full max-w-lg mx-auto" label="Select a file group" field=form.filegroup choices=form.filegroup.field.choices selected=form.filegroup.initial %} - {% form_input class="w-full max-w-lg mx-auto" label="Or create a new file group" field=form.new_filegroup %} + {% include "_components/form/select.html" with class="w-full max-w-lg mx-auto" label="Select a file group" field=form.filegroup choices=form.filegroup.field.choices selected=form.filegroup.initial %} + {% include "_components/form/input.html" with class="w-full max-w-lg mx-auto" label="Or create a new file group" field=form.new_filegroup %} {% endif %}
      - {% #button type="submit" variant="success" id="add-or-change-file-button" disabled=no_valid_files %}{{ modal_button_text }}{% /button %} - {% #button variant="danger" type="cancel" %}Cancel{% /button %} + {% component "_components/button.html" with type="submit" variant="success" id="add-or-change-file-button" disabled=no_valid_files %}{{ modal_button_text }}{% endcomponent %} + {% component "_components/button.html" with variant="danger" type="cancel" %}Cancel{% endcomponent %}
      - {% /card %} -{% /modal %} + {% endcomponent %} +{% endcomponent %} diff --git a/airlock/templates/all_workspaces.html b/airlock/templates/all_workspaces.html index 0294d842b..58d6b01db 100644 --- a/airlock/templates/all_workspaces.html +++ b/airlock/templates/all_workspaces.html @@ -7,7 +7,7 @@ {% block content %}
      - {% airlock_header title="All Workspaces" %} + {% include "_components/header/base.html" with title="All Workspaces" %} - {% #card %} - {% #list_group id="workspaces" %} + {% component "_components/card/card.html" %} + {% component "_components/list-group/list-group.html" with id="workspaces" %} {% for workspace in workspaces %} - {% #list_group_item href=workspace.get_url %}{{ workspace.display_name }}{% /list_group_item %} + {% component "_components/list-group/list-group-item.html" with href=workspace.get_url %}{{ workspace.display_name }}{% endcomponent %} {% empty %} - {% list_group_empty title="No workspaces found" description="No workspaces match your search" %} + {% include "_components/list-group/list-group-empty.html" with title="No workspaces found" description="No workspaces match your search" %} {% endfor %} - {% /list_group %} - {% /card %} + {% endcomponent %} + {% endcomponent %}
      diff --git a/airlock/templates/base.html b/airlock/templates/base.html index f51bc1f50..337781a13 100644 --- a/airlock/templates/base.html +++ b/airlock/templates/base.html @@ -31,7 +31,7 @@
      - {% alerts messages=messages %} + {% include "_partials/alerts.html" %} {% block content %}{% endblock %}
      {% block full_width_content %}{% endblock full_width_content %} diff --git a/airlock/templates/file_browser/_includes/file_content.html b/airlock/templates/file_browser/_includes/file_content.html index 26df69759..32dfa56f8 100644 --- a/airlock/templates/file_browser/_includes/file_content.html +++ b/airlock/templates/file_browser/_includes/file_content.html @@ -1,5 +1,5 @@ {% load static %} -{% #card id="fileCard" title=path_item.name container=False custom_button=buttons %} +{% component "_components/card/card.html" with id="fileCard" title=path_item.name container=False custom_button=buttons %}
      Loading... @@ -14,4 +14,4 @@ src="{{ path_item.contents_url }}" title="{{ path_item.relpath }}" > -{% /card %} +{% endcomponent %} diff --git a/airlock/templates/file_browser/_includes/uploaded_files_count.html b/airlock/templates/file_browser/_includes/uploaded_files_count.html index 8c6bb0c67..83295a4fe 100644 --- a/airlock/templates/file_browser/_includes/uploaded_files_count.html +++ b/airlock/templates/file_browser/_includes/uploaded_files_count.html @@ -8,9 +8,9 @@
      {{ release_request.uploaded_files_count }} {% if release_request.status.value == "RELEASED" %} - {% icon_check_circle_solid class="h-5 w-5 text-green-700" %} + {% include "_icons/check-circle-solid.svg" with class="h-5 w-5 text-green-700" %} {% elif upload_in_progress %} - {% icon_custom_spinner class="h-5 w-5 text-bn-egg-500 animate-spin stroke-current stroke-2" %} + {% include "_icons/custom/spinner.svg" with class="h-5 w-5 text-bn-egg-500 animate-spin stroke-current stroke-2" %} {% endif %}
      diff --git a/airlock/templates/file_browser/file_content/_includes/csv_summary.html b/airlock/templates/file_browser/file_content/_includes/csv_summary.html index e9e4bd67b..ef3789a65 100644 --- a/airlock/templates/file_browser/file_content/_includes/csv_summary.html +++ b/airlock/templates/file_browser/file_content/_includes/csv_summary.html @@ -3,7 +3,7 @@ #notes-list {list-style: circle; list-style-position: inside;} -{% #card container=True %} +{% component "_components/card/card.html" with container=True %}
      @@ -30,4 +30,4 @@ {{ summary.notes }} -{% /card %} +{% endcomponent %} diff --git a/airlock/templates/file_browser/file_content/csv.html b/airlock/templates/file_browser/file_content/csv.html index 1522eb866..3b108a98b 100644 --- a/airlock/templates/file_browser/file_content/csv.html +++ b/airlock/templates/file_browser/file_content/csv.html @@ -47,12 +47,12 @@ {% endfor %} {% endfragment %} - {% #clusterize_table header_row=header_row %} + {% component "_components/clusterize-table.html" with header_row=header_row %} {% for index, row in rows %} {% for cell in row %}{% endfor %} {% endfor %} - {% /clusterize_table %} + {% endcomponent %} {% else %}
      {{ index }}{{ cell }}
      diff --git a/airlock/templates/file_browser/file_content/text.html b/airlock/templates/file_browser/file_content/text.html index 5316fd0d1..1344e36e5 100644 --- a/airlock/templates/file_browser/file_content/text.html +++ b/airlock/templates/file_browser/file_content/text.html @@ -2,10 +2,10 @@ {% block metatitle %}{{ filename }}{% endblock %} {% block content %} {% if truncated|default:False %} - {% #alert variant="info" title="Log truncated" class="mt-4" no_icon=True %} + {% component "_components/alert/alert.html" with variant="info" title="Log truncated" class="mt-4" no_icon=True %} This log file is larger than the log viewing limit of {{ limit_kb }}kb, so we have only shown you the last {{ limit_kb }}kb. If you need to see more of the log, contact OpenSAFELY tech support. - {% /alert %} + {% endcomponent %} {% endif %}
      {{ text }}
      {% endblock %} diff --git a/airlock/templates/file_browser/repo/dir.html b/airlock/templates/file_browser/repo/dir.html index 0558fe7ad..f5378e0b4 100644 --- a/airlock/templates/file_browser/repo/dir.html +++ b/airlock/templates/file_browser/repo/dir.html @@ -2,7 +2,7 @@ {% load static %} {% load airlock %} -{% #card title=path_item.name container=True %} +{% component "_components/card/card.html" with title=path_item.name container=True %} {% fragment as header_row %} {% endfragment %} - {% #clusterize_table header_row=header_row %} + {% component "_components/clusterize-table.html" with header_row=header_row %} {% for path in path_item.children %} {% endfor %} - {% /clusterize_table %} -{% /card %} + {% endcomponent %} +{% endcomponent %} {% vite_hmr_client %} {% vite_asset "assets/src/scripts/datatable.js" %} diff --git a/airlock/templates/file_browser/repo/index.html b/airlock/templates/file_browser/repo/index.html index 0add7086b..e0cbd6f0e 100644 --- a/airlock/templates/file_browser/repo/index.html +++ b/airlock/templates/file_browser/repo/index.html @@ -1,6 +1,5 @@ {% extends "file_browser/index.html" %} {% block content %} - {% #airlock_repo_header current_request=current_request title=title workspace=workspace return_url=return_url %} - {% /airlock_repo_header %} + {% include "_components/header/repo/header.html" with current_request=current_request title=title workspace=workspace return_url=return_url %} {% endblock content %} diff --git a/airlock/templates/file_browser/request/dir.html b/airlock/templates/file_browser/request/dir.html index fd12b9916..fca08006d 100644 --- a/airlock/templates/file_browser/request/dir.html +++ b/airlock/templates/file_browser/request/dir.html @@ -6,14 +6,14 @@ {% if content_buttons.multiselect_withdraw.show %}
      - {% #button type="submit" name="action" value="withdraw_files" variant="warning" form="multiselect_form"%}Withdraw Files from Request{% /button %} + {% component "_components/button.html" with type="submit" name="action" value="withdraw_files" variant="warning" form="multiselect_form" %}Withdraw Files from Request{% endcomponent %}
      {% endif %} {% endfragment %} -{% #card title=path_item.name container=False custom_button=buttons %} +{% component "_components/card/card.html" with title=path_item.name container=False custom_button=buttons %} {% endfragment %} - {% #clusterize_table header_row=header_row %} + {% component "_components/clusterize-table.html" with header_row=header_row %} {% for path in path_item.children %} @@ -100,15 +100,15 @@ {% if content_buttons.multiselect_withdraw.show %} {% endif %} {% endfor %} - {% /clusterize_table %} + {% endcomponent %} -{% /card %} +{% endcomponent %} {% vite_hmr_client %} {% vite_asset "assets/src/scripts/datatable.js" %} diff --git a/airlock/templates/file_browser/request/file.html b/airlock/templates/file_browser/request/file.html index 3fc6b44d8..e0c5b68cc 100644 --- a/airlock/templates/file_browser/request/file.html +++ b/airlock/templates/file_browser/request/file.html @@ -4,13 +4,13 @@ Supporting - {% tooltip content="This is a supporting file and will not be released." %} + {% include "_components/tooltip.html" with content="This is a supporting file and will not be released." %} {% elif path_item.is_withdrawn %} Withdrawn - {% tooltip content="This file has been withdrawn and will not be released." %} + {% include "_components/tooltip.html" with content="This file has been withdrawn and will not be released." %} {% endif %} @@ -20,7 +20,7 @@
      Overall decision
      {{ decision.description }} - {% tooltip class="airlock-tooltip decision-status-tooltip" content=decision.reason %} + {% include "_components/tooltip.html" with class="airlock-tooltip decision-status-tooltip" content=decision.reason %} {% endwith %} @@ -30,7 +30,7 @@ {% csrf_token %} - {% #button type="submit" id="withdraw-file-button" tooltip=content_buttons.withdraw_file.tooltip variant="warning" %}Withdraw from Request{% /button %} + {% component "_components/button.html" with type="submit" id="withdraw-file-button" tooltip=content_buttons.withdraw_file.tooltip variant="warning" %}Withdraw from Request{% endcomponent %} {% endif %} @@ -41,9 +41,7 @@ hx-target="#multiselect_modal" hx-swap="outerHtml" > - {% #button type="submit" name="action" value="update_files" variant="success" id="update-file-modal-button" tooltip=content_buttons.change_file_properties_button.tooltip %} - Update file properties - {% /button %} + {% component "_components/button.html" with type="submit" name="action" value="update_files" variant="success" id="update-file-modal-button" tooltip=content_buttons.change_file_properties_button.tooltip %}Update file properties{% endcomponent %} {% csrf_token %} @@ -105,9 +103,9 @@ {% endif %} - {% #modal id="group-context" button_text="View Context & Comments" %} + {% component "_components/modal/modal.html" with id="group-context" button_text="View Context & Comments" %} {% include "file_browser/request/filegroup.html" %} - {% /modal %} + {% endcomponent %} {% include "file_browser/_includes/more_dropdown.html" with include_code=include_code include_download=include_download %} diff --git a/airlock/templates/file_browser/request/filegroup.html b/airlock/templates/file_browser/request/filegroup.html index 3e23b3393..4c6415497 100644 --- a/airlock/templates/file_browser/request/filegroup.html +++ b/airlock/templates/file_browser/request/filegroup.html @@ -19,41 +19,41 @@ {% if not group.inline %}
      {% if group.request_changes_button.show %} - {% #modal id="group-request-changes" button_small=True button_text="Request changes" button_tooltip=group.request_changes_button.tooltip button_variant="warning" %} - {% #card container=True title="Request changes for all remaining files in group "|add:group.name %} + {% component "_components/modal/modal.html" with id="group-request-changes" button_small=True button_text="Request changes" button_tooltip=group.request_changes_button.tooltip button_variant="warning" %} + {% component "_components/card/card.html" with container=True title="Request changes for all remaining files in group "|add:group.name %}
      {% csrf_token %}
      Request changes for all unreviewed output files in group {{ group.name }}. If you have already approved a file, your vote will NOT be changed.
      - {% #button type="submit" variant="warning" class="action-button" small=True id="group-request-changes-button" %}Request changes{% /button %} - {% #button variant="secondary-outline" type="cancel" small=True %}Cancel{% /button %} + {% component "_components/button.html" with type="submit" variant="warning" class="action-button" small=True id="group-request-changes-button" %}Request changes{% endcomponent %} + {% component "_components/button.html" with variant="secondary-outline" type="cancel" small=True %}Cancel{% endcomponent %} - {% /card %} - {% /modal %} + {% endcomponent %} + {% endcomponent %} {% endif %} {% if group.reset_votes_button.show %} - {% #modal id="group-reset-votes" button_small=True button_text="Reset votes" button_tooltip=group.reset_votes_button.tooltip %} - {% #card container=True title="Reset votes on all files in group "|add:group.name" %} + {% component "_components/modal/modal.html" with id="group-reset-votes" button_small=True button_text="Reset votes" button_tooltip=group.reset_votes_button.tooltip %} + {% component "_components/card/card.html" with container=True title="Reset votes on all files in group "|add:group.name %}
      {% csrf_token %}
      Reset all votes in group {{ group.name }}. This will reset all files that you have approved or requested changes for.
      - {% #button type="submit" variant="primary" class="action-button" small=True id="group-reset-votes-button" %}Reset votes{% /button %} - {% #button variant="secondary-outline" type="cancel" small=True %}Cancel{% /button %} + {% component "_components/button.html" with type="submit" variant="primary" class="action-button" small=True id="group-reset-votes-button" %}Reset votes{% endcomponent %} + {% component "_components/button.html" with variant="secondary-outline" type="cancel" small=True %}Cancel{% endcomponent %} - {% /card %} - {% /modal %} + {% endcomponent %} + {% endcomponent %} {% endif %}
      {% endif %} {% endfragment %} -{% #card title=group.title container=True class="group_modal" custom_button=buttons %} +{% component "_components/card/card.html" with title=group.title container=True class="group_modal" custom_button=buttons %}
      {% csrf_token %} - {% form_textarea field=group.c2_edit_form.context resize=True hint_below=True show_placeholder=True placeholder="Describe the data to be released in this group of files" class="w-full mx-auto" rows=6 disabled=group.c2_readonly readonly=group.c2_readonly %} - {% form_textarea field=group.c2_edit_form.controls resize=True hint_below=True show_placeholder=True placeholder="Describe the disclosure controls that have been applied to these files" class="w-full mx-auto" rows=6 disabled=group.c2_readonly readonly=group.c2_readonly %} + {% include "_components/form/textarea.html" with field=group.c2_edit_form.context resize=True hint_below=True show_placeholder=True placeholder="Describe the data to be released in this group of files" class="w-full mx-auto" rows=6 disabled=group.c2_readonly readonly=group.c2_readonly %} + {% include "_components/form/textarea.html" with field=group.c2_edit_form.controls resize=True hint_below=True show_placeholder=True placeholder="Describe the disclosure controls that have been applied to these files" class="w-full mx-auto" rows=6 disabled=group.c2_readonly readonly=group.c2_readonly %} {% if not group.c2_readonly %} - {% #button type="submit" variant="success" id="edit-group-button" class="w-min" disabled=group.c2_readonly %}Save{% /button %} + {% component "_components/button.html" with type="submit" variant="success" id="edit-group-button" class="w-min" disabled=group.c2_readonly %}Save{% endcomponent %} {% endif %}
      - {% #list_group id="comments" %} + {% component "_components/list-group/list-group.html" with id="comments" %} {% for comment, comment_class in group.comments %} {% fragment as comment_status %} {% if request.user.output_checker %} {% if release_request.get_turn_phase.name == "INDEPENDENT" and comment.review_turn == release_request.review_turn %} - {% #pill variant="info" text="Blinded" class="group" %} - {% comment%} + {% include "_components/pill/pill.html" with variant="info" text="Blinded" class="group" %} + {% comment %} TODO: get tooltips working for pills - {% tooltip content=comment.visibility.blinded_description %} - {% endcomment%} - {% /pill%} + include tooltip with content=comment.visibility.blinded_description + {% endcomment %} {% endif %} - {% #pill variant="info" class="group" text=comment.visibility.name.title %} - {% comment%} + {% include "_components/pill/pill.html" with variant="info" class="group" text=comment.visibility.name.title %} + {% comment %} TODO: get tooltips working for pills - {%tooltip content=comment.visibility.description %} - {% endcomment%} - {% /pill%} + include tooltip with content=comment.visibility.description + {% endcomment %} {% endif %} - {% pill variant="info" text=comment.created_at|date:"Y-m-d H:i" %} - {% pill variant="info" text=comment.review_turn %} + {% include "_components/pill/pill.html" with variant="info" text=comment.created_at|date:"Y-m-d H:i" %} + {% include "_components/pill/pill.html" with variant="info" text=comment.review_turn %} {% endfragment %} - {% fragment as author %}{% airlock_user user=comment.author %}{% endfragment %} - {% #list_group_rich_item custom_status=comment_status class=comment_class title=author %} + {% fragment as author %}{% include "_components/user.html" with user=comment.author %}{% endfragment %} + {% component "_components/list-group/list-group-rich-item.html" with custom_status=comment_status class=comment_class title=author %}
      {{ comment.comment|markdownify|linebreaks }}
      {% if request.user == comment.author %} {% if comment.visibility.name == "PRIVATE" and comment.review_turn == release_request.review_turn %} @@ -106,7 +104,7 @@
      {% csrf_token %} - {% #button variant="danger" type="submit" %}Make comment visible to all users{% /button %} + {% component "_components/button.html" with variant="danger" type="submit" %}Make comment visible to all users{% endcomponent %}
      {% endif %} @@ -115,57 +113,57 @@
      {% csrf_token %} - {% #button variant="danger" type="submit" %}Delete comment{% /button %} + {% component "_components/button.html" with variant="danger" type="submit" %}Delete comment{% endcomponent %}
      {% endif %} {% endif %} - {% /list_group_rich_item %} + {% endcomponent %} {% endfor %} {% if group.user_can_comment %} - {% #list_group_item %} + {% component "_components/list-group/list-group-item.html" %} {% csrf_token %} {% if group.comment_form.visibility.field.choices|length == 1 %} - {% #alert variant="info" title="Comments are pending" no_icon=True %} + {% component "_components/alert/alert.html" with variant="info" title="Comments are pending" no_icon=True %} Comments are initially only visible to you. Once you submit the request, comments will be visible to other users. - {% /alert %} + {% endcomponent %} {% endif %} {% if group.inline %} {% endif %} - {% form_textarea field=group.comment_form.comment placeholder="Use Markdown to format your comment" label="Add Comment" show_placeholder=True class="w-full max-w-lg" rows=6 required=False %} + {% include "_components/form/textarea.html" with field=group.comment_form.comment placeholder="Use Markdown to format your comment" label="Add Comment" show_placeholder=True class="w-full max-w-lg" rows=6 required=False %} {% if group.comment_form.visibility.field.choices|length == 1 %} {% else %} {% if group.comment_form.visibility.label %}
      - {% icon_exclamation_triangle_outline class="h-5 w-5 text-bn-flamenco-500 inline" %} + {% include "_icons/exclamation-triangle-outline.svg" with class="h-5 w-5 text-bn-flamenco-500 inline" %} {{ group.comment_form.visibility.label }}
      {% endif %} - {% form_radios field=group.comment_form.visibility choices=group.comment_form.visibility.field.choices class="w-full max-w-lg" %} + {% include "_components/form/radio_list.html" with field=group.comment_form.visibility choices=group.comment_form.visibility.field.choices class="w-full max-w-lg" %} {% endif %} {% if group.inline %}
      Comments will be annotated with a link to this file
      {% endif %}
      - {% #button type="submit" variant="success" id="edit-comment-button" %}Comment{% /button %} + {% component "_components/button.html" with type="submit" variant="success" id="edit-comment-button" %}Comment{% endcomponent %}
      - {% /list_group_item %} + {% endcomponent %} {% endif %} - {% /list_group %} + {% endcomponent %} {% if not group.inline %} {% include "activity.html" with activity=group.activity title="Recent activity for this group" %} {% else %} - {% #card_footer no_container=False %} - {% #button variant="primary" class="action-button" small=True type="cancel" %}Close{% /button %} - {% /card_footer %} + {% component "_components/card/card-footer.html" with no_container=False %} + {% component "_components/button.html" with variant="primary" class="action-button" small=True type="cancel" %}Close{% endcomponent %} + {% endcomponent %} {% endif %} -{% /card %} +{% endcomponent %} diff --git a/airlock/templates/file_browser/request/header.html b/airlock/templates/file_browser/request/header.html index 87f0da023..2c4ffc0c6 100644 --- a/airlock/templates/file_browser/request/header.html +++ b/airlock/templates/file_browser/request/header.html @@ -1,5 +1,5 @@
      - {% #airlock_request_header release_request=release_request title=title workspace=workspace group=group %} + {% component "_components/header/request/header.html" with release_request=release_request title=title workspace=workspace group=group %} {% if is_request_root %}

      Request overview

      {% else %} @@ -7,13 +7,13 @@

      Request overview

      ← Request overview {% endif %} - {% /airlock_request_header %} + {% endcomponent %} {% if request_action_required %}
      - {% #alert variant="warning" title="Action required" dismissible=True %} + {% component "_components/alert/alert.html" with variant="warning" title="Action required" dismissible=True %} {{ request_action_required }} - {% /alert %} + {% endcomponent %}
      {% endif %}
      diff --git a/airlock/templates/file_browser/request/request.html b/airlock/templates/file_browser/request/request.html index 36cf2a0a5..8f614a318 100644 --- a/airlock/templates/file_browser/request/request.html +++ b/airlock/templates/file_browser/request/request.html @@ -5,13 +5,13 @@
      {% if content_buttons.submit.show %} {% if content_buttons.submit.disabled %} - {% #button disabled=True class="relative group" small=True variant="success" id="submit-for-review-button" %} + {% component "_components/button.html" with disabled=True class="relative group" small=True variant="success" id="submit-for-review-button" %} Submit for review - {% tooltip class="airlock-tooltip" content=content_buttons.submit.tooltip %} - {% /button %} + {% include "_components/tooltip.html" with class="airlock-tooltip" content=content_buttons.submit.tooltip %} + {% endcomponent %} {% else %} - {% #modal id="submitRequest" button_small=True button_text="Submit for review" button_variant="success" %} - {% #card container=True title="Submit this request for review" %} + {% component "_components/modal/modal.html" with id="submitRequest" button_small=True button_text="Submit for review" button_variant="success" %} + {% component "_components/card/card.html" with container=True title="Submit this request for review" %}
      {% csrf_token %} @@ -31,32 +31,30 @@

      - {% #button type="submit" variant="success" class="action-button" small=True id="submit-for-review-button" %} - I confirm I have read the documentation - {% /button %} - {% #button variant="primary" class="action-button" small=True type="cancel" %}Cancel{% /button %} + {% component "_components/button.html" with type="submit" variant="success" class="action-button" small=True id="submit-for-review-button" %}I confirm I have read the documentation{% endcomponent %} + {% component "_components/button.html" with variant="primary" class="action-button" small=True type="cancel" %}Cancel{% endcomponent %} - {% /card %} - {% /modal %} + {% endcomponent %} + {% endcomponent %} {% endif %} {% endif %} {% if content_buttons.resubmit.show %} {% if content_buttons.resubmit.disabled %} - {% #button disabled=True class="relative group" small=True variant="success" id="resubmit-for-review-button" %} + {% component "_components/button.html" with disabled=True class="relative group" small=True variant="success" id="resubmit-for-review-button" %} Submit for review - {% tooltip class="airlock-tooltip" content=content_buttons.resubmit.tooltip %} - {% /button %} + {% include "_components/tooltip.html" with class="airlock-tooltip" content=content_buttons.resubmit.tooltip %} + {% endcomponent %} {% else %} {% comment %} Subsequent re-submission requires just a button press {% endcomment %} {% csrf_token %} - {% #button type="submit" tooltip=content_buttons.resubmit.tooltip variant="success" class="action-button" small=True id="resubmit-for-review-button" %}Submit for review{% /button %} + {% component "_components/button.html" with type="submit" tooltip=content_buttons.resubmit.tooltip variant="success" class="action-button" small=True id="resubmit-for-review-button" %}Submit for review{% endcomponent %} {% endif %} {% endif %} {% if content_buttons.withdraw.show %} - {% #modal id="withdrawRequest" button_small=True button_text="Withdraw this request" button_variant="warning" %} - {% #card container=True title="Withdraw this request" %} + {% component "_components/modal/modal.html" with id="withdrawRequest" button_small=True button_text="Withdraw this request" button_variant="warning" %} + {% component "_components/card/card.html" with container=True title="Withdraw this request" %} {% csrf_token %} @@ -66,34 +64,34 @@ recreated from scratch. Please confirm you wish to withdraw this request. - {% #button type="submit" variant="danger" class="action-button" small=True id="withdraw-request-confirm" %}Withdraw{% /button %} - {% #button variant="primary" type="cancel" small=True %}Cancel{% /button %} + {% component "_components/button.html" with type="submit" variant="danger" class="action-button" small=True id="withdraw-request-confirm" %}Withdraw{% endcomponent %} + {% component "_components/button.html" with variant="primary" type="cancel" small=True %}Cancel{% endcomponent %} - {% /card %} - {% /modal %} + {% endcomponent %} + {% endcomponent %} {% endif %} {% if content_buttons.submit_review.show %} {% if content_buttons.submit_review.disabled %} - {% #button disabled=True class="relative group" small=True variant="secondary" id="submit-review-button" %} + {% component "_components/button.html" with disabled=True class="relative group" small=True variant="secondary" id="submit-review-button" %} Submit review - {% tooltip class="airlock-tooltip" content=content_buttons.submit_review.tooltip %} - {% /button %} + {% include "_components/tooltip.html" with class="airlock-tooltip" content=content_buttons.submit_review.tooltip %} + {% endcomponent %} {% else %} {% csrf_token %} - {% #button type="submit" small=True tooltip=content_buttons.submit_review.tooltip variant="success" id="submit-review-button" %}Submit review{% /button %} + {% component "_components/button.html" with type="submit" small=True tooltip=content_buttons.submit_review.tooltip variant="success" id="submit-review-button" %}Submit review{% endcomponent %} {% endif %} {% endif %} {% if content_buttons.reject.show %} {% if content_buttons.reject.disabled %} - {% #button disabled=True class="relative group" small=True variant="danger" id="reject-request-button" data-modal="rejectRequest" %} + {% component "_components/button.html" with disabled=True class="relative group" small=True variant="danger" id="reject-request-button" data_modal="rejectRequest" %} Reject request - {% tooltip class="airlock-tooltip" content=content_buttons.reject.tooltip %} - {% /button %} + {% include "_components/tooltip.html" with class="airlock-tooltip" content=content_buttons.reject.tooltip %} + {% endcomponent %} {% else %} - {% #modal id="rejectRequest" button_small=True button_text="Reject request" button_variant="danger" button_tooltip=content_buttons.reject.tooltip %} - {% #card container=True title="Reject this request" %} + {% component "_components/modal/modal.html" with id="rejectRequest" button_small=True button_text="Reject request" button_variant="danger" button_tooltip=content_buttons.reject.tooltip %} + {% component "_components/card/card.html" with container=True title="Reject this request" %} {% csrf_token %} @@ -103,47 +101,47 @@ from scratch. Please confirm you wish to reject this request. - {% #button type="submit" variant="danger" class="action-button" small=True id="reject-request-button" %}Reject request{% /button %} - {% #button variant="primary" type="cancel" %}Cancel{% /button %} + {% component "_components/button.html" with type="submit" variant="danger" class="action-button" small=True id="reject-request-button" %}Reject request{% endcomponent %} + {% component "_components/button.html" with variant="primary" type="cancel" %}Cancel{% endcomponent %} - {% /card %} - {% /modal %} + {% endcomponent %} + {% endcomponent %} {% endif %} {% endif %} {% if content_buttons.return.show %} {% if content_buttons.return.disabled %} - {% #button disabled=True class="relative group" small=True id="return-request-button" %} + {% component "_components/button.html" with disabled=True class="relative group" small=True id="return-request-button" %} Return request - {% tooltip class="airlock-tooltip" content=content_buttons.return.tooltip %} - {% /button %} + {% include "_components/tooltip.html" with class="airlock-tooltip" content=content_buttons.return.tooltip %} + {% endcomponent %} {% else %} - {% #modal id="returnRequest" button_small=True button_text="Return request" button_tooltip=content_buttons.return.tooltip %} - {% #card container=True title="Return this request" %} + {% component "_components/modal/modal.html" with id="returnRequest" button_small=True button_text="Return request" button_tooltip=content_buttons.return.tooltip %} + {% component "_components/card/card.html" with container=True title="Return this request" %} {% csrf_token %}
      {{content_buttons.return.modal_confirm_message}}
      - {% #button type="submit" variant="warning" class="action-button" small=True id="return-request-button" %}Return request{% /button %} - {% #button variant="primary" type="cancel" small=True %}Cancel{% /button %} + {% component "_components/button.html" with type="submit" variant="warning" class="action-button" small=True id="return-request-button" %}Return request{% endcomponent %} + {% component "_components/button.html" with variant="primary" type="cancel" small=True %}Cancel{% endcomponent %} - {% /card %} - {% /modal %} + {% endcomponent %} + {% endcomponent %} {% endif %} {% endif %} {% if content_buttons.release_files.show %} {% if content_buttons.release_files.disabled %} - {% #button disabled=True class="relative group" small=True variant="warning" id="release-files-button" %} + {% component "_components/button.html" with disabled=True class="relative group" small=True variant="warning" id="release-files-button" %} Release files - {% tooltip class="airlock-tooltip" content=content_buttons.release_files.tooltip %} - {% /button %} + {% include "_components/tooltip.html" with class="airlock-tooltip" content=content_buttons.release_files.tooltip %} + {% endcomponent %} {% else %} {% csrf_token %} - {% #button small=True type="submit" tooltip=content_buttons.release_files.tooltip variant="warning" id="release-files-button" %} + {% component "_components/button.html" with small=True type="submit" tooltip=content_buttons.release_files.tooltip variant="warning" id="release-files-button" %} Release files - {% /button %} + {% endcomponent %} {% endif %} {% endif %} @@ -151,21 +149,21 @@ {% endfragment %} -{% #card title="Request: "|add:release_request.id custom_button=buttons %} - {% #description_list %} - {% #description_item title="Status" %} +{% component "_components/card/card.html" with title="Request: "|add:release_request.id custom_button=buttons %} + {% component "_components/description-list.html" %} + {% component "_components/description-item.html" with title="Status" %} {{ release_request.status.description }} - {% /description_item %} - {% #description_item title="Files requested for release" %} + {% endcomponent %} + {% component "_components/description-item.html" with title="Files requested for release" %} {{ release_request.output_files|length }} - {% /description_item %} - {% #description_item title="Supporting files not for release" %} + {% endcomponent %} + {% component "_components/description-item.html" with title="Supporting files not for release" %} {{ release_request.supporting_files_count }} - {% /description_item %} - {% #description_item title="Files released and uploaded" %} + {% endcomponent %} + {% component "_components/description-item.html" with title="Files released and uploaded" %} {% include "file_browser/_includes/uploaded_files_count.html" %} - {% /description_item %} - {% #description_item title="Filegroups" %} + {% endcomponent %} + {% component "_components/description-item.html" with title="Filegroups" %}
        {% for filegroup in release_request.filegroups.values %} {% if not filegroup.empty %} @@ -173,7 +171,7 @@
        {{ filegroup.name }} {% if filegroup.incomplete %} - {% icon_exclamation_triangle_outline class="h-5 w-5 text-bn-flamenco-500" %} + {% include "_icons/exclamation-triangle-outline.svg" with class="h-5 w-5 text-bn-flamenco-500" %} Information incomplete. Click on group name to enter context/controls. {% endif %}
        @@ -181,8 +179,8 @@ {% endif %} {% endfor %}
      - {% /description_item %} - {% /description_list %} -{% /card %} + {% endcomponent %} + {% endcomponent %} +{% endcomponent %} {% include "activity.html" with title="Recent activity for this request"%} diff --git a/airlock/templates/file_browser/workspace/dir.html b/airlock/templates/file_browser/workspace/dir.html index 22868266f..d51995b78 100644 --- a/airlock/templates/file_browser/workspace/dir.html +++ b/airlock/templates/file_browser/workspace/dir.html @@ -5,18 +5,12 @@ {% fragment as buttons %} {% if content_buttons.multiselect_add.show %} {% if content_buttons.multiselect_add.disabled %} - {% #button type="button" disabled=True tooltip=content_buttons.multiselect_add.tooltip id="add-file-modal-button" %} - Add Files to Request - {% /button %} + {% component "_components/button.html" with type="button" disabled=True tooltip=content_buttons.multiselect_add.tooltip id="add-file-modal-button" %}Add Files to Request{% endcomponent %} {% else %}
      - {% #button type="submit" name="action" value="add_files" variant="success" form="multiselect_form" id="add-file-modal-button" %} - Add Files to Request - {% /button %} - {% #button type="submit" name="action" value="update_files" variant="success" form="multiselect_form" id="update-file-modal-button" %} - Update Files in Request - {% /button %} + {% component "_components/button.html" with type="submit" name="action" value="add_files" variant="success" form="multiselect_form" id="add-file-modal-button" %}Add Files to Request{% endcomponent %} + {% component "_components/button.html" with type="submit" name="action" value="update_files" variant="success" form="multiselect_form" id="update-file-modal-button" %}Update Files in Request{% endcomponent %}
      @@ -25,7 +19,7 @@ {% endfragment %} -{% #card title=path_item.name container=False custom_button=buttons %} +{% component "_components/card/card.html" with title=path_item.name container=False custom_button=buttons %} {% endfragment %} - {% #clusterize_table header_row=header_row %} + {% component "_components/clusterize-table.html" with header_row=header_row %} {% for path in path_item.children %} @@ -113,15 +107,15 @@ {% if not content_buttons.multiselect_add.disabled %} {% endif %} {% endfor %} - {% /clusterize_table %} + {% endcomponent %} -{% /card %} +{% endcomponent %} {% vite_hmr_client %} {% vite_asset "assets/src/scripts/datatable.js" %} diff --git a/airlock/templates/file_browser/workspace/file.html b/airlock/templates/file_browser/workspace/file.html index 04e52be28..2189d24cb 100644 --- a/airlock/templates/file_browser/workspace/file.html +++ b/airlock/templates/file_browser/workspace/file.html @@ -3,13 +3,9 @@ {% if content_buttons.add_file_button.show %} {% if content_buttons.add_file_button.disabled %} {% if content_buttons.add_file %} - {% #button type="button" disabled=True tooltip=content_buttons.add_file_button.tooltip id="add-file-modal-button" %} - Add File to Request - {% /button %} + {% component "_components/button.html" with type="button" disabled=True tooltip=content_buttons.add_file_button.tooltip id="add-file-modal-button" %}Add File to Request{% endcomponent %} {% else %} - {% #button type="button" disabled=True tooltip=content_buttons.add_file_button.tooltip id="update-file-modal-button" %} - Update File in Request - {% /button %} + {% component "_components/button.html" with type="button" disabled=True tooltip=content_buttons.add_file_button.tooltip id="update-file-modal-button" %}Update File in Request{% endcomponent %} {% endif %} {% else %} {% if content_buttons.add_file %} - {% #button type="submit" name="action" value="add_files" variant="success" id="add-file-modal-button" tooltip=content_buttons.add_file_button.tooltip %} - Add File to Request - {% /button %} + {% component "_components/button.html" with type="submit" name="action" value="add_files" variant="success" id="add-file-modal-button" tooltip=content_buttons.add_file_button.tooltip %}Add File to Request{% endcomponent %} {% else %} - {% #button type="submit" name="action" value="update_files" variant="success" id="update-file-modal-button" tooltip=content_buttons.add_file_button.tooltip %} - Update File in Request - {% /button %} + {% component "_components/button.html" with type="submit" name="action" value="update_files" variant="success" id="update-file-modal-button" tooltip=content_buttons.add_file_button.tooltip %}Update File in Request{% endcomponent %} {% endif %} @@ -41,9 +33,10 @@ {% endfragment %} {% if path_item.metadata.out_of_date_action %} -
      {% #alert variant="warning" title="Out of date action" class="my-2 p-2" dismissible=False %} - This file was produced by an action that is no longer present in the workspace's project.yaml - {% /alert %} +
      + {% component "_components/alert/alert.html" with variant="warning" title="Out of date action" class="my-2 p-2" dismissible=False %} + This file was produced by an action that is no longer present in the workspace's project.yaml + {% endcomponent %}
      {% endif %} diff --git a/airlock/templates/file_browser/workspace/index.html b/airlock/templates/file_browser/workspace/index.html index fac9740ed..baa150d48 100644 --- a/airlock/templates/file_browser/workspace/index.html +++ b/airlock/templates/file_browser/workspace/index.html @@ -1,7 +1,5 @@ {% extends "file_browser/index.html" %} {% block content %} - {% #airlock_workspace_header current_request=current_request title=title workspace=workspace %} - {% /airlock_workspace_header %} + {% include "_components/header/workspace/header.html" with current_request=current_request title=title workspace=workspace %} {% endblock content %} - diff --git a/airlock/templates/file_browser/workspace/workspace.html b/airlock/templates/file_browser/workspace/workspace.html index b81c957b8..d519e6f70 100644 --- a/airlock/templates/file_browser/workspace/workspace.html +++ b/airlock/templates/file_browser/workspace/workspace.html @@ -1,10 +1,10 @@ -{% #card title=workspace.display_name %} - {% #description_list %} - {% #description_item title="Project" %} +{% component "_components/card/card.html" with title=workspace.display_name %} + {% component "_components/description-list.html" %} + {% component "_components/description-item.html" with title="Project" %} {{ project.display_name }} - {% /description_item %} - {% #description_item title="Organisation" %} + {% endcomponent %} + {% component "_components/description-item.html" with title="Organisation" %} {{ project.display_organisations }} - {% /description_item %} - {% /description_list %} -{% /card %} + {% endcomponent %} + {% endcomponent %} +{% endcomponent %} diff --git a/airlock/templates/login.html b/airlock/templates/login.html index b88be5ff6..608f489a8 100644 --- a/airlock/templates/login.html +++ b/airlock/templates/login.html @@ -5,7 +5,7 @@ {% block metatitle %}Login | Airlock{% endblock metatitle %} {% block content %} - {% airlock_header title="Login" %} + {% include "_components/header/base.html" with title="Login" %}

      To login to Airlock, you need to:

      @@ -20,27 +20,25 @@

      Once you have logged in once, you should not need to again, unless you do not access Airlock for a long time.

      - {% #card title="Login with Single Use Token" subtitle="Log in from Level 4 environments with a Single Use Token" container=True class="max-w-2xl" %} + {% component "_components/card/card.html" with title="Login with Single Use Token" subtitle="Log in from Level 4 environments with a Single Use Token" container=True class="max-w-2xl" %} {% if dev_users_file %} - {% #alert variant="warning" title="Development login mode" class="mb-6" dismissible=True %} + {% component "_components/alert/alert.html" with variant="warning" title="Development login mode" class="mb-6" dismissible=True %} Use credentials specified in:
      {{ dev_users_file }} - {% /alert %} + {% endcomponent %} {% endif %} {% csrf_token %} - {% form_input type="text" field=token_login_form.user required=True label="GitHub username or OpenSAFELY email address" placeholder="opensafely" class="w-full max-w-md" %} - {% form_input type="text" field=token_login_form.token required=True label="Single Use Token" placeholder="three random words" show_placeholder=True class="w-full max-w-md" %} + {% include "_components/form/input.html" with type="text" field=token_login_form.user required=True label="GitHub username or OpenSAFELY email address" placeholder="opensafely" class="w-full max-w-md" %} + {% include "_components/form/input.html" with type="text" field=token_login_form.token required=True label="Single Use Token" placeholder="three random words" show_placeholder=True class="w-full max-w-md" %} - {% #button type="submit" variant="success" id="login-button" class="mt-4" %} - Log in with token - {% /button %} + {% component "_components/button.html" with type="submit" variant="success" id="login-button" class="mt-4" %}Log in with token{% endcomponent %} - {% /card %} + {% endcomponent %} {% endblock content %} diff --git a/airlock/templates/requests_for_output_checker.html b/airlock/templates/requests_for_output_checker.html index c1235a356..cea0b810a 100644 --- a/airlock/templates/requests_for_output_checker.html +++ b/airlock/templates/requests_for_output_checker.html @@ -6,13 +6,13 @@ {% block content %}
      - {% airlock_header title="Requests for review" %} + {% include "_components/header/base.html" with title="Requests for review" %} - {% #card title="Outstanding requests awaiting review" %} - {% #list_group id="outstanding-requests" %} + {% component "_components/card/card.html" with title="Outstanding requests awaiting review" %} + {% component "_components/list-group/list-group.html" with id="outstanding-requests" %} {% for release_request, request_progress in outstanding_requests %} - {% fragment as title %}{{ release_request.get_short_id }} by {% airlock_user user=release_request.author %}{% endfragment %} - {% #airlock_list_group_rich_item type="Outstanding request" last_submitted_date=release_request.last_submitted_at title=title url=release_request.get_url status_text=release_request.status.description %} + {% fragment as title %}{{ release_request.get_short_id }} by {% include "_components/user.html" with user=release_request.author %}{% endfragment %} + {% component "_components/list-group/list-group-rich-item.html" with type="Outstanding request" last_submitted_date=release_request.last_submitted_at title=title url=release_request.get_url status_text=release_request.status.description %}
      Workspace:
      @@ -31,18 +31,18 @@ {{ release_request.display_organisations }}
      - {% /airlock_list_group_rich_item %} + {% endcomponent %} {% empty %} - {% list_group_empty title="No outstanding requests" description="There are no outstanding requests awaiting review" %} + {% include "_components/list-group/list-group-empty.html" with title="No outstanding requests" description="There are no outstanding requests awaiting review" %} {% endfor %} - {% /list_group %} - {% /card %} + {% endcomponent %} + {% endcomponent %} - {% #card title="Requests returned for changes/questions" %} - {% #list_group id="returned-requests" %} + {% component "_components/card/card.html" with title="Requests returned for changes/questions" %} + {% component "_components/list-group/list-group.html" with id="returned-requests" %} {% for release_request in returned_requests %} {% fragment as title %}{{ release_request.get_short_id }}{% endfragment %} - {% #airlock_list_group_rich_item type="Returned request" title=title url=release_request.get_url status_text=release_request.status.description %} + {% component "_components/list-group/list-group-rich-item.html" with type="Returned request" title=title url=release_request.get_url status_text=release_request.status.description %}
      Workspace:
      @@ -57,19 +57,19 @@ {{ release_request.display_organisations }}
      - {% /airlock_list_group_rich_item %} + {% endcomponent %} {% empty %} - {% list_group_empty title="No returned requests" description="There are no returned requests awaiting re-submission" %} + {% include "_components/list-group/list-group-empty.html" with title="No returned requests" description="There are no returned requests awaiting re-submission" %} {% endfor %} - {% /list_group %} - {% /card %} + {% endcomponent %} + {% endcomponent %} {% if approved_requests %} - {% #card title="Approved requests awaiting release" %} - {% #list_group id="returned-requests" %} + {% component "_components/card/card.html" with title="Approved requests awaiting release" %} + {% component "_components/list-group/list-group.html" with id="returned-requests" %} {% for release_request in approved_requests %} {% fragment as title %}{{ release_request.get_short_id }}{% endfragment %} - {% #airlock_list_group_rich_item type="Approved request" title=title url=release_request.get_url status_text=release_request.status.description %} + {% component "_components/list-group/list-group-rich-item.html" with type="Approved request" title=title url=release_request.get_url status_text=release_request.status.description %}
      Workspace:
      @@ -84,10 +84,10 @@ {{ release_request.display_organisations }}
      - {% /airlock_list_group_rich_item %} + {% endcomponent %} {% endfor %} - {% /list_group %} - {% /card %} + {% endcomponent %} + {% endcomponent %} {% endif %}
      diff --git a/airlock/templates/requests_for_workspace.html b/airlock/templates/requests_for_workspace.html index 1ec97fedf..4c82dbcbd 100644 --- a/airlock/templates/requests_for_workspace.html +++ b/airlock/templates/requests_for_workspace.html @@ -7,9 +7,9 @@ {% fragment as custom_button %} {% if request_filter %}
      - {{ request_filter|title }} {% icon_x_circle_solid class="text-oxford-600 h-5 w-5" %} + {{ request_filter|title }} {% include "_icons/x-circle-solid.svg" with class="text-oxford-600 h-5 w-5" %} - {% tooltip position="-bottom-3" content="Clear filter" %} + {% include "_components/tooltip.html" with position="-bottom-3" content="Clear filter" %}
      {% endif %} @@ -26,19 +26,19 @@ {% endfragment %} - {% #card title="All requests in Workspace: "|add:workspace custom_button=custom_button %} - {% #list_group id="requests-workspace" %} + {% component "_components/card/card.html" with title="All requests in Workspace: "|add:workspace custom_button=custom_button %} + {% component "_components/list-group/list-group.html" with id="requests-workspace" %} {% for request in requests_for_workspace|dictsortreversed:"created_at" %} - {% fragment as title %}{{ request.get_short_id }} by {% airlock_user user=request.author %}{% endfragment %} - {% #airlock_list_group_rich_item title=title url=request.get_url status_text=request.status.description %} + {% fragment as title %}{{ request.get_short_id }} by {% include "_components/user.html" with user=request.author %}{% endfragment %} + {% component "_components/list-group/list-group-rich-item.html" with title=title url=request.get_url status_text=request.status.description %}
      - {% airlock_list_group_rich_item created_date=request.created_at last_submitted_date=request.last_submitted_at %} -
      - {% /airlock_list_group_rich_item %} + {% include "_components/list-group/list-group-rich-item.html" with created_date=request.created_at last_submitted_date=request.last_submitted_at %} +
      + {% endcomponent %} - {% empty %} - {% list_group_empty title="No requests" description="There are no requests in this workspace" %} - {% endfor %} - {% /list_group %} - {% /card %} - {% endblock content %} + {% empty %} + {% include "_components/list-group/list-group-empty.html" with title="No requests" description="There are no requests in this workspace" %} + {% endfor %} + {% endcomponent %} + {% endcomponent %} +{% endblock content %} diff --git a/airlock/templates/update_files.html b/airlock/templates/update_files.html index e65f2da5d..018fc1f6e 100644 --- a/airlock/templates/update_files.html +++ b/airlock/templates/update_files.html @@ -1,37 +1,35 @@ {# hide default button, as we are going to autoshow the modal anyway #} {% fragment as button %} {% endfragment %} -{% #modal id="updateRequestFile" custom_button=button autoshow=True %} - {% #card container=True title="Update Files in Request" %} +{% component "_components/modal/modal.html" with id="updateRequestFile" custom_button=button autoshow=True %} + {% component "_components/card/card.html" with container=True title="Update Files in Request" %}
      {% csrf_token %} {{ form.next_url }} {{ formset.management_form }} - {% #list_group %} + {% component "_components/list-group/list-group.html" %} {% for name, reason in files_ignored.items %} - {% #list_group_item %} + {% component "_components/list-group/list-group-item.html" %}
      {{ name }} {{ reason }}
      - {% /list_group_item %} + {% endcomponent %} {% endfor %} {% for formset_form in formset %} - {% #list_group_item %} + {% component "_components/list-group/list-group-item.html" %}
      {{ formset_form.file.value }} {{ formset_form.file }}
      - {% /list_group_item %} + {% endcomponent %} {% endfor %} - {% /list_group %} + {% endcomponent %}
      - {% #button type="submit" variant="success" id="update-file-button" disabled=no_valid_files %} - Update Files in Request - {% /button %} - {% #button variant="danger" type="cancel" %}Cancel{% /button %} + {% component "_components/button.html" with type="submit" variant="success" id="update-file-button" disabled=no_valid_files %}Update Files in Request{% endcomponent %} + {% component "_components/button.html" with variant="danger" type="cancel" %}Cancel{% endcomponent %}
      - {% /card %} -{% /modal %} + {% endcomponent %} +{% endcomponent %} diff --git a/airlock/templates/workspaces.html b/airlock/templates/workspaces.html index f1c89ab6e..d3fcbc9d8 100644 --- a/airlock/templates/workspaces.html +++ b/airlock/templates/workspaces.html @@ -1,12 +1,12 @@ {% extends "base.html" %} -{% block metatitle %}{{ workspace_header }} {% airlock_user user=request.user %} | Airlock{% endblock metatitle %} +{% block metatitle %}{{ workspace_header }} {% include "_components/user.html" with user=request.user %} | Airlock{% endblock metatitle %} {% block content %}
      - {% airlock_header title=workspace_header|add:request.user.fullname %} + {% include "_components/header/base.html" with title=workspace_header|add:request.user.fullname %} {% for project, workspaces in projects.items %} - {% #card %} + {% component "_components/card/card.html" %}
      @@ -15,63 +15,62 @@ {% fragment as project_title %} {{ project.display_name }} {% for org in project.orgs %} - {% pill variant="info" text=org %} + {% include "_components/pill/pill.html" with variant="info" text=org %} {% endfor %} {% endfragment %} - {% card title=project_title custom_button=custom_button %} - - {% if workspace_type == "workspaces" %} - {% for workspace in workspaces %} -
      - - {% fragment as button %} - {% endfragment %} - {% #list_group id="workspaces" %} - {% #airlock_list_dropdown custom_button=button %} -
      - {{ workspace.display_name }} -
      - {% /airlock_list_dropdown %} - {% /list_group %} -
      - -
        - {% #list_group id="authored-requests" %} - {% for release_request in authored_requests|dictsortreversed:"created_at" %} - {% if workspace.display_name == release_request.workspace %} -
        - {% airlock_list_group_rich_item type="Request" created_date=release_request.created_at last_submitted_date=release_request.last_submitted_at title=release_request.get_short_id url=release_request.get_url status_text=release_request.status.description %} -
        - {% endif %} - {% empty %} - {% list_group_empty title="No requests" description="You do not have any authored requests" %} - {% endfor %} - {% /list_group %} -
      -
      - {% endfor %} - {% else%} + {% include "_components/card/card.html" with title=project_title custom_button=custom_button %} + + {% if workspace_type == "workspaces" %} + {% for workspace in workspaces %} +
      + + {% fragment as button %} + {% endfragment %} + {% component "_components/list-group/list-group.html" with id="workspaces" %} + {% component "_components/list-group/list-group-item-dropdown.html" with custom_button=button %} +
      + {{ workspace.display_name }} +
      + {% endcomponent %} + {% endcomponent %} +
      +
        -
      • - {% #list_group id="workspaces" %} - {% for workspace in workspaces %} - {% #list_group_item href=workspace.get_url %}{{ workspace.display_name }}{% /list_group_item %} - {% endfor %} - {% /list_group %} -
      • + {% component "_components/list-group/list-group.html" with id="authored-requests" %} + {% for release_request in authored_requests|dictsortreversed:"created_at" %} + {% if workspace.display_name == release_request.workspace %} +
        + {% include "_components/list-group/list-group-rich-item.html" with type="Request" created_date=release_request.created_at last_submitted_date=release_request.last_submitted_at title=release_request.get_short_id url=release_request.get_url status_text=release_request.status.description %} +
        + {% endif %} + {% empty %} + {% include "_components/list-group/list-group-empty.html" with title="No requests" description="You do not have any authored requests" %} + {% endfor %} + {% endcomponent %}
      - - {% endif %}
      + {% endfor %} + {% else%} +
        +
      • + {% component "_components/list-group/list-group.html" with id="workspaces" %} + {% for workspace in workspaces %} + {% component "_components/list-group/list-group-item.html" with href=workspace.get_url %}{{ workspace.display_name }}{% endcomponent %} + {% endfor %} + {% endcomponent %} +
      • +
      - {% /card %} - {% empty %} - {% #card title=workspace_type.title container=True %} - {% list_group_empty title="No "|add:workspace_type|add:" available" description="You do not have access to any "|add:workspace_type %} - {% /card %} - {% endfor %} -
      - {% endblock content %} + {% endif %} + + {% endcomponent %} + {% empty %} + {% component "_components/card/card.html" with title=workspace_type.title container=True %} + {% include "_components/list-group/list-group-empty.html" with title="No "|add:workspace_type|add:" available" description="You do not have access to any "|add:workspace_type %} + {% endcomponent %} + {% endfor %} +
      +{% endblock content %} diff --git a/airlock/templatetags/airlock_components.py b/airlock/templatetags/airlock_components.py deleted file mode 100644 index 430366cb5..000000000 --- a/airlock/templatetags/airlock_components.py +++ /dev/null @@ -1,21 +0,0 @@ -from django import template -from slippers.templatetags.slippers import register_components # type: ignore - - -register = template.Library() - - -register_components( - { - "airlock_header": "_components/header/base.html", - "airlock_workspace_header": "_components/header/workspace/header.html", - "airlock_request_header": "_components/header/request/header.html", - "airlock_repo_header": "_components/header/repo/header.html", - "datatable": "_components/datatable.html", - "clusterize_table": "_components/clusterize-table.html", - "airlock_user": "_components/user.html", - "airlock_list_dropdown": "_components/list-group/list-group-item-dropdown.html", - "airlock_list_group_rich_item": "_components/list-group/list-group-rich-item.html", - }, - register, -) diff --git a/airlock/templatetags/template_components.py b/airlock/templatetags/template_components.py new file mode 100644 index 000000000..b3f52dee2 --- /dev/null +++ b/airlock/templatetags/template_components.py @@ -0,0 +1,206 @@ +from django import template +from django.utils.html import conditional_escape, format_html +from django.utils.safestring import mark_safe + + +register = template.Library() + + +class AttrsNode(template.Node): + def __init__(self, items): + # items: list of (html_attr_name, filter_expr_or_None, context_key) + self.items = items + + def render(self, context): + # Look up positional args only in the topmost context dict — that's the + # {% include %}'s pushed extra_context. Without this restriction, an + # outer-scope variable like `form` (a Django Form) leaks into a child + # component's attribute lookup. + top = context.dicts[-1] if context.dicts else {} + parts = [] + for html_name, expr, ctx_key in self.items: + if expr is not None: + value = expr.resolve(context) + else: + value = top.get(ctx_key) + + if value is None or value is False or value == "": + continue + + if value is True: + parts.append(conditional_escape(html_name)) + else: + parts.append(format_html('{}="{}"', html_name, value)) + + return " ".join(parts) + + +@register.tag("attrs") +def attrs_tag(parser, token): + """ + Render HTML attributes from template context variables. + + Usage: {% attrs name1 name2 name3=expr %} + + - Positional args: look up the name in the topmost context dict (the + include's pushed extra_context) only — skip if falsy or absent. + - Keyword args: evaluate expr in full context (supports filters like + |default:). + - Underscores in names are converted to hyphens in HTML output + (data_modal → data-modal, hx_post → hx-post) + - Boolean True renders as a bare attribute (disabled, required, multiple) + - False/None/empty string are skipped + """ + bits = token.split_contents() + items = [] + for bit in bits[1:]: + if "=" in bit: + name, _, expr_str = bit.partition("=") + html_name = name.replace("_", "-") + expr = parser.compile_filter(expr_str) + items.append((html_name, expr, name)) + else: + html_name = bit.replace("_", "-") + items.append((html_name, None, bit)) + return AttrsNode(items) + + +class SetVarNode(template.Node): + def __init__(self, assignments): + self.assignments = assignments + + def render(self, context): + for name, expr in self.assignments: + context[name] = expr.resolve(context) + return "" + + +@register.tag("setvar") +def setvar_tag(parser, token): + """ + Set template variables without a closing tag. + + Usage: {% setvar name=value %} or {% setvar name=var|filter %} + + Replaces Slippers' {% var %} tag. Sets variables in the current context + layer, accessible for the remainder of the template. + """ + bits = token.split_contents() + if len(bits) < 2: + raise template.TemplateSyntaxError( + f"{bits[0]} requires at least one name=value argument" + ) + assignments = [] + for bit in bits[1:]: + if "=" not in bit: + raise template.TemplateSyntaxError( + f"{bits[0]}: expected name=value, got {bit!r}" + ) + name, _, expr_str = bit.partition("=") + assignments.append((name, parser.compile_filter(expr_str))) + return SetVarNode(assignments) + + +class FragmentNode(template.Node): + def __init__(self, nodelist, var_name): + self.nodelist = nodelist + self.var_name = var_name + + def render(self, context): + context[self.var_name] = mark_safe(self.nodelist.render(context)) + return "" + + +@register.tag("fragment") +def fragment_tag(parser, token): + """ + Capture rendered template content into a context variable. + + Usage: {% fragment as varname %}...{% endfragment %} + + Replaces Slippers' {% fragment %} tag. The rendered content is stored + as a safe HTML string in the context, available for the rest of the + template as {{ varname }}. + """ + bits = token.split_contents() + if len(bits) != 3 or bits[1] != "as": + raise template.TemplateSyntaxError( + f"{bits[0]} expects 'as varname', got: {' '.join(bits[1:])!r}" + ) + var_name = bits[2] + nodelist = parser.parse(("endfragment",)) + parser.delete_first_token() + return FragmentNode(nodelist, var_name) + + +_BODY_KEY = "__component_body__" + + +class ComponentNode(template.Node): + def __init__(self, template_name, nodelist, extra_context): + self.template_name = template_name + self.nodelist = nodelist + self.extra_context = extra_context + + def render(self, context): + body = mark_safe(self.nodelist.render(context)) + values = { + name: expr.resolve(context) for name, expr in self.extra_context.items() + } + # Stash body under a sentinel key. {% body %} reads it from the topmost + # context dict only, which prevents the outer component's body from + # leaking into nested {% include %}s that don't push their own body. + values[_BODY_KEY] = body + tmpl_name = self.template_name.resolve(context) + tmpl = context.template.engine.get_template(tmpl_name) + with context.push(**values): + return tmpl.render(context) + + +@register.tag("component") +def component_tag(parser, token): + """ + Render a component template, capturing the block's content as the body slot. + + Usage: {% component "path.html" with key=value ... %}...{% endcomponent %} + + Inside the component template, use {% body %} to render the slot content. + """ + bits = token.split_contents() + if len(bits) < 2: + raise template.TemplateSyntaxError(f"{bits[0]} requires a template path") + template_name = parser.compile_filter(bits[1]) + + extra_context = {} + i = 2 + if i < len(bits): + if bits[i] != "with": + raise template.TemplateSyntaxError( + f"{bits[0]} expects 'with' before extra arguments, got {bits[i]!r}" + ) + i += 1 + while i < len(bits): + bit = bits[i] + if "=" not in bit: + raise template.TemplateSyntaxError( + f"{bits[0]} 'with' args must be name=value, got {bit!r}" + ) + name, _, expr_str = bit.partition("=") + extra_context[name] = parser.compile_filter(expr_str) + i += 1 + + nodelist = parser.parse(("endcomponent",)) + parser.delete_first_token() + return ComponentNode(template_name, nodelist, extra_context) + + +@register.simple_tag(takes_context=True) +def body(context): + """ + Render the body slot of the surrounding {% component %} invocation. + + Reads only from the topmost context dict (the component's pushed values), + so an outer component's body never leaks into nested {% include %}s. + """ + top = context.dicts[-1] if context.dicts else {} + return top.get(_BODY_KEY, "") diff --git a/airlock/urls.py b/airlock/urls.py index 9cb1afffb..7826ba13c 100644 --- a/airlock/urls.py +++ b/airlock/urls.py @@ -21,14 +21,12 @@ import airlock.views import airlock.views.code -import assets.views urlpatterns = [ path("", RedirectView.as_view(pattern_name="workspace_index"), name="home"), path("login/", airlock.views.login, name="login"), path("logout/", airlock.views.logout, name="logout"), - path("ui-components/", assets.views.components), # workspaces path( "workspaces/", diff --git a/assets/base_views.py b/assets/base_views.py deleted file mode 100644 index 9b0e7d90d..000000000 --- a/assets/base_views.py +++ /dev/null @@ -1,54 +0,0 @@ -from datetime import UTC, datetime - -from django import forms -from django.template.response import TemplateResponse - - -class ExampleForm(forms.Form): - - example_select = forms.ChoiceField( - choices=[ - ["", "Please select a language"], - ["english", "English"], - ["french", "French"], - ["german", "German"], - ["spanish", "Spanish"], - ], - label="Language select", - initial="french", - ) - - example_email = forms.EmailField() - - example_textarea = forms.Textarea() - - example_radios = forms.ChoiceField( - choices=[ - ["english", "English"], - ["french", "French"], - ["german", "German"], - ["spanish", "Spanish"], - ], - label="Language select", - initial="french", - widget=forms.RadioSelect, - ) - - def clean_example_email(self): - self.add_error( - "example_email", "This email is registered to a different account" - ) - - -def components(request): - example_date = datetime.fromtimestamp(1667317153, tz=UTC) - form = ExampleForm({"example_select": "french", "example_email": "you@example.com"}) - form.is_valid() - return TemplateResponse( - request, - "_components/index.html", - context={ - "example_date": example_date, - "example_form": form, - }, - ) diff --git a/assets/justfile b/assets/justfile index 73111923c..a76c9194d 100644 --- a/assets/justfile +++ b/assets/justfile @@ -9,34 +9,3 @@ default: # Remove built assets and collected static files clean: rm -rf $ASSETS_DIST - - -# Update upstream assets from job-server -update JOBSERVER_DIR="": clean - #!/bin/bash - set -euo pipefail - - jobserver_dir={{ JOBSERVER_DIR }} - - if [ -z "$jobserver_dir" ]; then - jobserver_dir=$(mktemp -d) - git clone --depth=1 https://github.com/opensafely-core/job-server "$jobserver_dir"; - else - # If we have a jobserver_dir, make sure it's an absolute path, otherwise calls to - # the job-server --justfile below may fail unexpectedly, because we're likely to - # be calling this justfile from one directory up (as in just/assets update). - if [ ! "${jobserver_dir:0:1}" = "/" ]; then - echo "JOBSERVER_DIR must be an absolute path to your local job-server directory" - exit 1 - fi - fi - - just -d "$jobserver_dir" --justfile "$jobserver_dir"/justfile assets-install - just -d "$jobserver_dir" --justfile "$jobserver_dir"/justfile assets-build - mkdir -p dist templates - cp -r "$jobserver_dir"/templates/_components/* ./templates/_components/ - cp "$jobserver_dir"/jobserver/views/components.py ./base_views.py - mkdir -p dist - cp -r "$jobserver_dir"/assets/dist/* ./dist/ - cp -r "$jobserver_dir"/assets/dist/.vite ./dist/ - diff --git a/assets/templates/_components/alert/alert.html b/assets/templates/_components/alert/alert.html index 6cb137515..27464f3ad 100644 --- a/assets/templates/_components/alert/alert.html +++ b/assets/templates/_components/alert/alert.html @@ -1,27 +1,27 @@ diff --git a/assets/templates/_components/article/header.html b/assets/templates/_components/article/header.html deleted file mode 100644 index c5f277448..000000000 --- a/assets/templates/_components/article/header.html +++ /dev/null @@ -1,11 +0,0 @@ -
      -

      - {{ title }} -

      - {% if text %} -
      - {{ text }} -
      - {% endif %} - {{ children }} -
      diff --git a/assets/templates/_components/breadcrumbs/breadcrumb.html b/assets/templates/_components/breadcrumbs/breadcrumb.html deleted file mode 100644 index d9604bdc7..000000000 --- a/assets/templates/_components/breadcrumbs/breadcrumb.html +++ /dev/null @@ -1,30 +0,0 @@ -
    • - {% if title == "Home" %} - - {% icon_home_outline class="flex-shrink-0 h-5 w-5" %} - {{ title }} - - {% else %} - {% if url %} - - {{ title }} - - {% if location %} - {% tooltip position="-bottom-2" content=location %} - {% endif %} - - {% else %} - - {{ title }} - - {% if location %} - {% tooltip position="-bottom-2" content=location %} - {% endif %} - - {% endif %} - {% endif %} - - {% if not active %} - {% icon_chevron_right_outline class="flex-shrink-0 h-5 w-5 text-oxford/25" %} - {% endif %} -
    • diff --git a/assets/templates/_components/breadcrumbs/breadcrumbs-container.html b/assets/templates/_components/breadcrumbs/breadcrumbs-container.html deleted file mode 100644 index fed017c89..000000000 --- a/assets/templates/_components/breadcrumbs/breadcrumbs-container.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/assets/templates/_components/button.html b/assets/templates/_components/button.html index d93d6c3f3..1798ccb23 100644 --- a/assets/templates/_components/button.html +++ b/assets/templates/_components/button.html @@ -1,13 +1,13 @@ {% if type == "link" %} - {{ children }} + {% body %} {% if tooltip %} - {% tooltip position="-bottom-4" content=tooltip %} + {% include "_components/tooltip.html" with position="-bottom-4" content=tooltip %} {% endif %} {% if type == "link" %} diff --git a/assets/templates/_components/card/card-footer.html b/assets/templates/_components/card/card-footer.html index d9c27a34e..9e720defa 100644 --- a/assets/templates/_components/card/card-footer.html +++ b/assets/templates/_components/card/card-footer.html @@ -9,5 +9,5 @@ {{ class }} " > - {{ children }} + {% body %}
      diff --git a/assets/templates/_components/card/card.html b/assets/templates/_components/card/card.html index e819bd54e..4282be6fe 100644 --- a/assets/templates/_components/card/card.html +++ b/assets/templates/_components/card/card.html @@ -1,6 +1,12 @@ +{# Re-bind the slot variables locally and clear the inherited values so #} +{# nested components don't accidentally pick them up from outer context. #} +{% setvar local_button=button %} +{% setvar local_custom_button=custom_button %} +{% setvar button="" %} +{% setvar custom_button="" %}
      - {% if title or subtitle or button %} + {% if title or subtitle or local_button %}
      {% if title %} @@ -13,13 +19,11 @@

      {{ subtitle }}

      {% endif %}

      - {% if button %} - {% #button class="flex-shrink-0" href=button_href type="link" variant="primary" %} - {{ button_text }} - {% /button %} + {% if local_button %} + {% component "_components/button.html" with class="flex-shrink-0" href=button_href type="link" variant="primary" %}{{ button_text }}{% endcomponent %} {% endif %} - {% if custom_button %} - {{ custom_button }} + {% if local_custom_button %} + {{ local_custom_button }} {% endif %}
      {% endif %} @@ -33,7 +37,7 @@

      {% endif %} - {{ children }} + {% body %} {% if container %}

      {% endif %} diff --git a/assets/templates/_components/code.html b/assets/templates/_components/code.html index f8ed83d07..e3acba304 100644 --- a/assets/templates/_components/code.html +++ b/assets/templates/_components/code.html @@ -1 +1 @@ -{{ children }} +{% body %} diff --git a/assets/templates/_components/description-item.html b/assets/templates/_components/description-item.html index 9fc05c114..83f5ba1b7 100644 --- a/assets/templates/_components/description-item.html +++ b/assets/templates/_components/description-item.html @@ -1,5 +1,5 @@ -{% var title_class=title_class|default:"font-bold sm:text-sm sm:font-semibold text-slate-900" %} -{% var details_class=details_class|default:"mt-1 text-sm text-slate-900 pl-3 ml-0.5 border-l-2 border-l-slate-200 sm:pl-0 sm:ml-0 sm:border-l-0 sm:col-span-3 sm:mt-0" %} +{% setvar title_class=title_class|default:"font-bold sm:text-sm sm:font-semibold text-slate-900" %} +{% setvar details_class=details_class|default:"mt-1 text-sm text-slate-900 pl-3 ml-0.5 border-l-2 border-l-slate-200 sm:pl-0 sm:ml-0 sm:border-l-0 sm:col-span-3 sm:mt-0" %}
      {{ title }} -
      {{ children }}
      +
      {% body %}
      diff --git a/assets/templates/_components/description-list.html b/assets/templates/_components/description-list.html index e324f6550..fdd615592 100644 --- a/assets/templates/_components/description-list.html +++ b/assets/templates/_components/description-list.html @@ -1,3 +1,3 @@
      - {{ children }} + {% body %}
      diff --git a/assets/templates/_components/footer.html b/assets/templates/_components/footer.html deleted file mode 100644 index a6c0157ad..000000000 --- a/assets/templates/_components/footer.html +++ /dev/null @@ -1,103 +0,0 @@ -{% load static %} - -
      diff --git a/assets/templates/_components/form/checkbox.html b/assets/templates/_components/form/checkbox.html index 52cbe9a43..50f7741ce 100644 --- a/assets/templates/_components/form/checkbox.html +++ b/assets/templates/_components/form/checkbox.html @@ -1,13 +1,13 @@ {% if custom_field == True %} - {% var label_for=id %} - {% var input_id=id %} - {% var input_name=name %} - {% var input_value=value %} + {% setvar label_for=id %} + {% setvar input_id=id %} + {% setvar input_name=name %} + {% setvar input_value=value %} {% else %} - {% var label_for=field.id_for_label %} - {% var input_id=field.auto_id %} - {% var input_name=field.html_name %} - {% var input_value=field.value %} + {% setvar label_for=field.id_for_label %} + {% setvar input_id=field.auto_id %} + {% setvar input_name=field.html_name %} + {% setvar input_value=field.value %} {% endif %}
      - {{ children }} - diff --git a/assets/templates/_components/table/table-cell.html b/assets/templates/_components/table/table-cell.html deleted file mode 100644 index e4b33825d..000000000 --- a/assets/templates/_components/table/table-cell.html +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/assets/templates/_components/table/table-head.html b/assets/templates/_components/table/table-head.html deleted file mode 100644 index a379f7e12..000000000 --- a/assets/templates/_components/table/table-head.html +++ /dev/null @@ -1,3 +0,0 @@ - - {{ children }} - diff --git a/assets/templates/_components/table/table-header.html b/assets/templates/_components/table/table-header.html deleted file mode 100644 index 4e6e07eda..000000000 --- a/assets/templates/_components/table/table-header.html +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/assets/templates/_components/table/table-pagination.html b/assets/templates/_components/table/table-pagination.html deleted file mode 100644 index 8e2f9cf9a..000000000 --- a/assets/templates/_components/table/table-pagination.html +++ /dev/null @@ -1,29 +0,0 @@ -{% if paginator %} - {% var page_number=paginator.number %} - {% var total_pages=paginator.paginator.num_pages %} - {% var has_previous=paginator.has_previous %} - {% var has_next=paginator.has_next %} -{% endif %} - - diff --git a/assets/templates/_components/table/table-row.html b/assets/templates/_components/table/table-row.html deleted file mode 100644 index fe7132846..000000000 --- a/assets/templates/_components/table/table-row.html +++ /dev/null @@ -1 +0,0 @@ -{{ children }} diff --git a/assets/templates/_components/table/table.html b/assets/templates/_components/table/table.html deleted file mode 100644 index a351bfa8e..000000000 --- a/assets/templates/_components/table/table.html +++ /dev/null @@ -1,3 +0,0 @@ -
      @@ -17,14 +17,14 @@
      {{ path.name }}
      {{ path.name }} {% if not path.is_directory %} - {% form_checkbox name="selected" value=path.relpath custom_field=True %} + {% include "_components/form/checkbox.html" with name="selected" value=path.relpath custom_field=True %} {% endif %}
      {{ path.name }} {% if not path.is_directory %} - {% form_checkbox name="selected" value=path.relpath custom_field=True %} + {% include "_components/form/checkbox.html" with name="selected" value=path.relpath custom_field=True %} {% endif %}
      - {{ children }} -
      - {{ children }} -
      - {{ children }} -
      diff --git a/assets/templates/_components/time.html b/assets/templates/_components/time.html deleted file mode 100644 index 453fd3220..000000000 --- a/assets/templates/_components/time.html +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/assets/templates/_components/timeline-item.html b/assets/templates/_components/timeline-item.html deleted file mode 100644 index f268e6070..000000000 --- a/assets/templates/_components/timeline-item.html +++ /dev/null @@ -1,48 +0,0 @@ -{% load humanize %} - -
    • - {% if not last %} - - {% endif %} -
      - - {{ children }} - -
      - {% if time or content %} -

      - {{ title }} - {% if time %} - - {% else %} - - {{ content }} - - {% endif %} -

      - {% else %} -

      {{ title }}

      - {% endif %} -
      -
      -
    • diff --git a/assets/templates/_components/tooltip.html b/assets/templates/_components/tooltip.html index 030ac80f3..586d41f87 100644 --- a/assets/templates/_components/tooltip.html +++ b/assets/templates/_components/tooltip.html @@ -1,7 +1,7 @@ {% if tooltip_top %} - {% var classes="after:-top-3 before:-bottom-1" %} + {% setvar classes="after:-top-3 before:-bottom-1" %} {% else %} - {% var classes="after:-top-3 before:-top-1" %} + {% setvar classes="after:-top-3 before:-top-1" %} {% endif %} diff --git a/assets/templates/_partials/alerts.html b/assets/templates/_partials/alerts.html index 3c44293a3..91d1b3b2b 100644 --- a/assets/templates/_partials/alerts.html +++ b/assets/templates/_partials/alerts.html @@ -1,46 +1,36 @@ {% if messages or disable_creating_jobs %}
      {% if disable_creating_jobs %} - {% #alert variant="warning" title="Job creation issue" %} + {% component "_components/alert/alert.html" with variant="warning" title="Job creation issue" %} Creating jobs is temporarily unavailable, please try again later. - {% /alert %} + {% endcomponent %} {% endif %} {% if messages %}
      {% for message in messages %} {% if message.tags == "alert-info" %} - {% #alert variant="info" dismissible=True %} - {{ message|safe }} - {% /alert %} + {% component "_components/alert/alert.html" with variant="info" dismissible=True %}{{ message|safe }}{% endcomponent %} {% elif message.tags == "alert-success" %} - {% #alert variant="success" dismissible=True %} - {{ message|safe }} - {% /alert %} + {% component "_components/alert/alert.html" with variant="success" dismissible=True %}{{ message|safe }}{% endcomponent %} {% elif message.tags == "alert-warning" %} - {% #alert variant="warning" dismissible=True %} - {{ message|safe }} - {% /alert %} + {% component "_components/alert/alert.html" with variant="warning" dismissible=True %}{{ message|safe }}{% endcomponent %} {% elif message.tags == "alert-danger" %} - {% #alert variant="danger" dismissible=True %} - {{ message|safe }} - {% /alert %} + {% component "_components/alert/alert.html" with variant="danger" dismissible=True %}{{ message|safe }}{% endcomponent %} {% elif message.tags == "codelist_out_of_date" %} - {% #alert variant="warning" title="Codelists for this workspace are out of date." %} + {% component "_components/alert/alert.html" with variant="warning" title="Codelists for this workspace are out of date." %}

      This will cause actions that use the backend database to fail. You can fix this issue by re-running - {% #code %}opensafely codelists update{% /code %}. + {% component "_components/code.html" %}opensafely codelists update{% endcomponent %}.

      For more details, please see the documentation on - {% link href="https://docs.opensafely.org/codelist-updating/" text="keeping codelists up to date" append_after="." %} + {% include "_components/link.html" with href="https://docs.opensafely.org/codelist-updating/" text="keeping codelists up to date" append_after="." %}

      - {% /alert %} + {% endcomponent %} {% else %} - {% #alert variant="info" dismissible=True %} - {{ message|safe }} - {% /alert %} + {% component "_components/alert/alert.html" with variant="info" dismissible=True %}{{ message|safe }}{% endcomponent %} {% endif %} {% endfor %}
      diff --git a/assets/templates/_partials/card-pagination.html b/assets/templates/_partials/card-pagination.html deleted file mode 100644 index 0801b9288..000000000 --- a/assets/templates/_partials/card-pagination.html +++ /dev/null @@ -1,43 +0,0 @@ -{% load querystring_tools %} - -{% if page_obj.has_next %} - {% url_with_querystring page=page_obj.paginator.num_pages as last_page_url %} - {% url_with_querystring page=page_obj.next_page_number as next_page_url %} -{% endif %} - -{% if page_obj.has_previous %} - {% url_with_querystring page=1 as first_page_url %} - {% url_with_querystring page=page_obj.previous_page_number as previous_page_url %} -{% endif %} - -{% var page_number=page_obj.number %} -{% var total_pages=page_obj.paginator.num_pages %} - -{% #card_footer no_container=no_container %} - -{% /card_footer %} diff --git a/assets/templates/_partials/latest-job-requests-table.html b/assets/templates/_partials/latest-job-requests-table.html deleted file mode 100644 index 47d351e0d..000000000 --- a/assets/templates/_partials/latest-job-requests-table.html +++ /dev/null @@ -1,72 +0,0 @@ -{% load humanize %} - -{% url "job-list" as job_list_url %} -
      - {% #card class="mx-4" no_container=True title="Latest job requests" subtitle="Most recent job requests on the OpenSAFELY platform" button=True button_text="View all job requests" button_href=job_list_url %} -
      -
      -
      -
      - {% #table %} - {% #table_head %} - {% #table_row %} - {% #table_header %}Status{% /table_header %} - {% #table_header nowrap=True %}Organisation{% /table_header %} - {% #table_header nowrap=True %}Project{% /table_header %} - {% #table_header nowrap=True %}User{% /table_header %} - {% #table_header nowrap=True %}Started{% /table_header %} - {% #table_header %}View request{% /table_header %} - {% /table_row %} - {% /table_head %} - {% #table_body %} - {% for job_request in job_requests %} - {% #table_row %} - {% #table_cell class="py-2 pl-4 pr-3" title=job_request.status|title nowrap=True %} - {% if job_request.status == "succeeded" %} - {% icon_check_circle_solid class="h-6 w-6 text-green-700" %} - {% elif job_request.status == "pending" %} - {% icon_clock_outline class="h-6 w-6 text-slate-500 stroke-2" %} - {% elif job_request.status == "running" %} - {% icon_custom_spinner class="h-6 w-6 animate-spin stroke-oxford-600 stroke-2 text-oxford-300" %} - {% elif job_request.status == "failed" %} - {% icon_x_circle_solid class="h-6 w-6 text-bn-ribbon-700" %} - {% endif %} - {{ job_request.status|title }} - {% /table_cell %} - {% #table_cell class="max-w-[25ch]" %} - {{ job_request.workspace.project.org }} - {% /table_cell %} - {% #table_cell class="break-words min-w-[18ch]" %} - {{ job_request.workspace.project.name }} - {% /table_cell %} - {% #table_cell nowrap=True %} - {{ job_request.created_by.name }} - {% /table_cell %} - {% #table_cell nowrap=True title=job_request.started_at %} - - {% if job_request.started_at|naturalday == "today" %} - {{ job_request.started_at|naturaltime }} - {% elif job_request.started_at %} - {{ job_request.started_at|date:"d M Y" }} - at - {{ job_request.started_at|date:"H:i" }} - {% else %} - - - {% endif %} - - {% /table_cell %} - {% #table_cell nowrap=True %} - {% #button type="link" href=job_request.get_absolute_url variant="secondary-outline" small=True %} - View, Job request {{ job_request.id }} - {% /button %} - {% /table_cell %} - {% /table_row %} - {% endfor %} - {% /table_body %} - {% /table %} -
      -
      -
      -
      - {% /card %} -
      diff --git a/assets/templates/_partials/staff-hero.html b/assets/templates/_partials/staff-hero.html deleted file mode 100644 index 536836790..000000000 --- a/assets/templates/_partials/staff-hero.html +++ /dev/null @@ -1,7 +0,0 @@ -
      -
      - {% #article_header title=title text=text %} - {{ children }} - {% /article_header %} -
      -
      diff --git a/assets/templates/components.yaml b/assets/templates/components.yaml deleted file mode 100644 index 0a7108d42..000000000 --- a/assets/templates/components.yaml +++ /dev/null @@ -1,95 +0,0 @@ -components: - # Components - article_header: "_components/article/header.html" - alert: "_components/alert/alert.html" - breadcrumb: "_components/breadcrumbs/breadcrumb.html" - breadcrumbs: "_components/breadcrumbs/breadcrumbs-container.html" - button: "_components/button.html" - card: "_components/card/card.html" - card_footer: "_components/card/card-footer.html" - code: "_components/code.html" - description_list: "_components/description-list.html" - description_item: "_components/description-item.html" - footer: "_components/footer.html" - form_checkbox: "_components/form/checkbox.html" - form_fieldset: "_components/form/fieldset.html" - form_input: "_components/form/input.html" - form_legend: "_components/form/legend.html" - form_radio: "_components/form/radio.html" - form_radios: "_components/form/radio_list.html" - form_select: "_components/form/select.html" - form_textarea: "_components/form/textarea.html" - grid_three_cols: "_components/grid/three-cols.html" - grid_col_span_1: "_components/grid/col-span-1.html" - grid_col_lg_span_2: "_components/grid/col-lg-span-2.html" - header: "_components/header.html" - link: "_components/link.html" - list_group: "_components/list-group/list-group.html" - list_group_empty: "_components/list-group/list-group-empty.html" - list_group_item: "_components/list-group/list-group-item.html" - list_group_rich_item: "_components/list-group/list-group-rich-item.html" - log_item: "_components/log-item.html" - modal: "_components/modal/modal.html" - multiselect: "_components/multiselect/multiselect.html" - multiselect_option: "_components/multiselect/multiselect-option.html" - pill: "_components/pill/pill.html" - pill_application_status: "_components/pill/application-status.html" - pill_project_status: "_components/pill/project-status.html" - skip_link: "_components/skip-link.html" - table_body: "_components/table/table-body.html" - table_cell: "_components/table/table-cell.html" - table_head: "_components/table/table-head.html" - table_header: "_components/table/table-header.html" - table_pagination: "_components/table/table-pagination.html" - table_row: "_components/table/table-row.html" - table: "_components/table/table.html" - time: "_components/time.html" - timeline_item: "_components/timeline-item.html" - tooltip: "_components/tooltip.html" - - # Partials - alerts: "_partials/alerts.html" - card_pagination: "_partials/card-pagination.html" - latest_job_requests_table: "_partials/latest-job-requests-table.html" - staff_hero: "_partials/staff-hero.html" - - # Icons - icon_academic_cap_outline: "_icons/academic-cap-outline.svg" - icon_arrow_down_mini: "_icons/arrow-down-mini.svg" - icon_arrow_up_mini: "_icons/arrow-up-mini.svg" - icon_arrows_up_down_mini: "_icons/arrows-up-down-mini.svg" - icon_beaker_outline: "_icons/beaker-outline.svg" - icon_branches_outline: "_icons/branches-outline.svg" - icon_building_library_outline: "_icons/building-library-outline.svg" - icon_calendar_outline: "_icons/calendar-outline.svg" - icon_check_circle_solid: "_icons/check-circle-solid.svg" - icon_check_outline: "_icons/check-outline.svg" - icon_chevron_down_mini: "_icons/chevron-down-mini.svg" - icon_chevron_right_outline: "_icons/chevron-right-outline.svg" - icon_chevron_up_mini: "_icons/chevron-up-mini.svg" - icon_clipboard_document_check_outline: "_icons/clipboard-document-check-outline.svg" - icon_clock_outline: "_icons/clock-outline.svg" - icon_clock_solid: "_icons/clock-solid.svg" - icon_code_bracket_outline: "_icons/code-bracket-outline.svg" - icon_custom_spinner: "_icons/custom/spinner.svg" - icon_ellipsis_horizontal_outline: "_icons/ellipsis-horizontal-outline.svg" - icon_exclamation_circle_mini: "_icons/exclamation-circle-mini.svg" - icon_exclamation_triangle_outline: "_icons/exclamation-triangle-outline.svg" - icon_exclamation_triangle_solid: "_icons/exclamation-triangle-solid.svg" - icon_folder_open_outline: "_icons/folder-open-outline.svg" - icon_folder_outline: "_icons/folder-outline.svg" - icon_github_outline: "_icons/github-outline.svg" - icon_home_outline: "_icons/home-outline.svg" - icon_information_circle_solid: "_icons/information-circle-solid.svg" - icon_lifebuoy_outline: "_icons/lifebuoy-outline.svg" - icon_lock_closed_solid: "_icons/lock-closed-solid.svg" - icon_menu_outline: "_icons/menu-outline.svg" - icon_pencil_outline: "_icons/pencil-outline.svg" - icon_play_outline: "_icons/play-outline.svg" - icon_queue_list_outline: "_icons/queue-list-outline.svg" - icon_rectangle_stack_outline: "_icons/rectangle-stack-outline.svg" - icon_status_online_outline: "_icons/status-online-outline.svg" - icon_user_group_outline: "_icons/user-group-outline.svg" - icon_user_outline: "_icons/user-outline.svg" - icon_x_circle_solid: "_icons/x-circle-solid.svg" - icon_x_outline: "_icons/x-outline.svg" diff --git a/assets/test_components.py b/assets/test_components.py deleted file mode 100644 index c82b5e0e8..000000000 --- a/assets/test_components.py +++ /dev/null @@ -1,3 +0,0 @@ -def test_components_browser(client): - response = client.get("/ui-components/") - assert response.status_code == 200 diff --git a/assets/views.py b/assets/views.py deleted file mode 100644 index 8fe78ac44..000000000 --- a/assets/views.py +++ /dev/null @@ -1,8 +0,0 @@ -from airlock.views.helpers import login_exempt - -from .base_views import components as upstream_components - - -@login_exempt -def components(request): - return upstream_components(request) diff --git a/pyproject.toml b/pyproject.toml index 3d62e3810..6ab2f585c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,9 +7,8 @@ dependencies = [ "ansi2html<=1.9.2", - "Django<=5.2.14", + "Django<=6.0.5", "django-vite<=3.1.0", - "slippers<=0.7.0", "django-htmx<=1.27.0", "django-markdownify<=0.9.6", "whitenoise<=6.12.0", @@ -32,7 +31,6 @@ # Exclude upstream assets files from jobserver exclude = [ "assets/templates/*", - "assets/base_views.py" ] [tool.ruff.lint] @@ -89,11 +87,6 @@ exclude_also = [ [tool.coverage.django_coverage_plugin] template_extensions = "html" -exclude_blocks = [ - # exclude block lines for end of slippers component (blocks that - # start with \ e.g. {% \button %} - "^\\/\\w+", -] [tool.ruff.lint.per-file-ignores] "airlock/views/__init__.py" = ["F401"] @@ -111,9 +104,6 @@ exclude = [ # Don't follow the import chain into the modules containing code vendored from elsewhere [[tool.mypy.overrides]] -module = "assets.base_views" -follow_imports = "skip" -[[tool.mypy.overrides]] module = "services.tracing" follow_imports = "skip" [[tool.mypy.overrides]] diff --git a/renovate.json5 b/renovate.json5 index 58f0efe05..a131523fe 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -120,11 +120,6 @@ // Otherwise renovate will use the general "widen" strategy to change the upper limit in pyproject.toml // The locked version will correctly remain at the latest allowed version for python 3.11 // i.e. <6, but the upper limit in pytproject.toml is confusing - { - "matchManagers": ["pep621"], - "matchDepNames": ["Django"], - "allowedVersions": "<6.0" - }, { "matchManagers": ["github-actions"], "groupName": "GitHub Actions", diff --git a/requirements.uvmirror b/requirements.uvmirror index e0e277245..e57e4d00e 100644 --- a/requirements.uvmirror +++ b/requirements.uvmirror @@ -37,7 +37,7 @@ coverage==7.13.5 # pytest-cov distro==1.9.0 # via ruyaml -django==5.2.14 +django==6.0.5 # via # airlock # django-coverage-plugin @@ -48,7 +48,6 @@ django==5.2.14 # django-stubs # django-stubs-ext # django-vite - # slippers django-coverage-plugin @ https://github.com/opensafely-core/django_coverage_plugin/archive/75209df74c37178666868abbd9687a41aaba0e13.zip django-debug-toolbar==6.3.0 # via django-debug-toolbar-template-profiler @@ -232,7 +231,6 @@ pyyaml==6.0.3 # pymdown-extensions # pyyaml-env-tag # responses - # slippers pyyaml-env-tag==1.1 # via mkdocs requests==2.33.1 @@ -250,8 +248,6 @@ setuptools==82.0.1 # via ruyaml six==1.17.0 # via python-dateutil -slippers==0.7.0 - # via airlock sortedcontainers==2.4.0 # via hypothesis sqlparse==0.5.5 @@ -262,8 +258,6 @@ text-unidecode==1.3 # via python-slugify tinycss2==1.4.0 # via bleach -typeguard==2.13.3 - # via slippers types-pyyaml==6.0.12.20260408 # via django-stubs types-requests==2.33.0.20260408 @@ -280,7 +274,6 @@ typing-extensions==4.15.0 # pydantic # pydantic-core # pyee - # slippers # typing-inspection typing-inspection==0.4.2 # via pydantic diff --git a/uv.lock b/uv.lock index f52f57642..a4256fa36 100644 --- a/uv.lock +++ b/uv.lock @@ -23,7 +23,6 @@ dependencies = [ { name = "opentelemetry-sdk" }, { name = "pydantic" }, { name = "requests" }, - { name = "slippers" }, { name = "ulid" }, { name = "whitenoise" }, ] @@ -52,7 +51,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "ansi2html", specifier = "<=1.9.2" }, - { name = "django", specifier = "<=5.2.14" }, + { name = "django", specifier = "<=6.0.5" }, { name = "django-extensions", specifier = "<=4.1" }, { name = "django-htmx", specifier = "<=1.27.0" }, { name = "django-markdownify", specifier = "<=0.9.6" }, @@ -67,7 +66,6 @@ requires-dist = [ { name = "opentelemetry-sdk", specifier = "<=1.41.0" }, { name = "pydantic", specifier = "<=2.13.3" }, { name = "requests", specifier = "<=2.33.1" }, - { name = "slippers", specifier = "<=0.7.0" }, { name = "ulid", specifier = "<=1.1" }, { name = "whitenoise", specifier = "<=6.12.0" }, ] @@ -260,16 +258,16 @@ wheels = [ [[package]] name = "django" -version = "5.2.14" +version = "6.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asgiref" }, { name = "sqlparse" }, { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/95/95f7faa0950867afaa0bef2460c6263afd6a2c78cc9434046ed28160b015/django-5.2.14.tar.gz", hash = "sha256:58a63ba841662e5c686b57ba1fec52ddd68c0b93bd96ac3029d55728f00bf8a2", size = 10895118, upload-time = "2026-05-05T13:57:31.104Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/f1/bf85f0d29ef76abf901f193fe8fef4769d3da7794197832bc30151c071d8/django-6.0.5.tar.gz", hash = "sha256:bc6d6872e98a2864c836e42edd644b362db311147dd5aa8d5b82ba7a032f5269", size = 10924131, upload-time = "2026-05-05T13:54:39.329Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/44/f172870cf87aa25afef48fb72adba89ee8b77fcab6f3b23d240b923f1528/django-5.2.14-py3-none-any.whl", hash = "sha256:6f712143bd3064310d1f50fac859c3e9a274bdcfc9595339853be7779297fc76", size = 8311320, upload-time = "2026-05-05T13:57:25.795Z" }, + { url = "https://files.pythonhosted.org/packages/94/5b/1328f8b84fce040c404f76822bf8c57d254e368e8cbd8bd67ec2b26d75f5/django-6.0.5-py3-none-any.whl", hash = "sha256:9d58a7cb49244e74c8e161d5e403a46d6209f1009ba40f5a66d6aa0d0786a8f0", size = 8368680, upload-time = "2026-05-05T13:54:33.532Z" }, ] [[package]] @@ -1271,21 +1269,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] -[[package]] -name = "slippers" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "django" }, - { name = "pyyaml" }, - { name = "typeguard" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/96/95bc00d151c2bcc2a669b6313e6a9cb902c6638258f2109e1cba3b625a0d/slippers-0.7.0.tar.gz", hash = "sha256:35059d4025845a46e252633a16a04136f21b28469dee7c4a2a361e00aee5335e", size = 11014, upload-time = "2026-04-04T13:51:38.841Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/63/a5d1c88e297b57152ec6e166ea2d7a31c0fcf0858a2a65490b91141610e2/slippers-0.7.0-py3-none-any.whl", hash = "sha256:49c67577939cf310283904199f405db481a08d6c43406ab5df01871b50a5758a", size = 13336, upload-time = "2026-04-04T13:51:37.596Z" }, -] - [[package]] name = "sortedcontainers" version = "2.4.0" @@ -1325,15 +1308,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610, upload-time = "2024-10-24T14:58:28.029Z" }, ] -[[package]] -name = "typeguard" -version = "2.13.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/38/c61bfcf62a7b572b5e9363a802ff92559cb427ee963048e1442e3aef7490/typeguard-2.13.3.tar.gz", hash = "sha256:00edaa8da3a133674796cf5ea87d9f4b4c367d77476e185e80251cc13dfbb8c4", size = 40604, upload-time = "2021-12-10T21:09:39.158Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/bb/d43e5c75054e53efce310e79d63df0ac3f25e34c926be5dffb7d283fb2a8/typeguard-2.13.3-py3-none-any.whl", hash = "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1", size = 17605, upload-time = "2021-12-10T21:09:37.844Z" }, -] - [[package]] name = "types-pyyaml" version = "6.0.12.20260408"