Skip to content

Commit 1a65b35

Browse files
authored
Add organization team and team member view templates (#226)
* Add organization team and team member view templates Refs #222 * Fix memberlist placeholder paragraph alignment
1 parent dfd1e3f commit 1a65b35

25 files changed

+517
-56
lines changed

readthedocsext/theme/static/readthedocsext/theme/js/site.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{% comment %}
2+
This is a small include for the breadcrumb on the top of the team subview pages.
3+
4+
:param team: Team object for breadcrumb
5+
:param last_active: Set the last item as active? False if the view has more
6+
breadcrumbs to add. Default: ``True``
7+
{% endcomment %}
8+
9+
<span class="section">Teams</span>
10+
<span class="divider">/</span>
11+
<span class="{% if last_active|default_if_none:True %}active{% endif %} section">
12+
{{ team.name }}
13+
</span>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{% extends "organizations/base.html" %}
2+
3+
{% load i18n %}
4+
{% load gravatar %}
5+
{% load organizations %}
6+
7+
{% block title %}{{ organization.name }} - {% trans "Members" %}{% endblock %}
8+
9+
{% block content-header %}
10+
{% include "organizations/partials/header.html" with organization=organization members_active="active" is_collapsed=False %}
11+
{% endblock %}
12+
13+
{% block content %}
14+
{% include "organizations/partials/members_list.html" with objects=members %}
15+
{% endblock content %}

readthedocsext/theme/templates/organizations/organization_detail.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
{% block title %}{{ organization.name }}{% endblock %}
88

99
{% block content-header %}
10-
{% include "organizations/partials/header.html" with organization=organization teams_active="active" is_collapsed=False %}
10+
{% include "organizations/partials/header.html" with organization=organization projects_active="active" is_collapsed=True %}
1111
{% endblock %}
1212

1313
{% block content %}
14-
{% include "organizations/partials/team_list.html" with objects=teams %}
14+
{% include "organizations/partials/project_list.html" with objects=projects %}
1515
{% endblock content %}

readthedocsext/theme/templates/organizations/partials/header.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,15 @@
106106

107107
{% block organization_header_navigation %}
108108
<div class="ui bottom attached stackable menu">
109-
<a class="item {{ teams_active }}" href="{{ organization.get_absolute_url }}">
109+
<a class="item {{ projects_active }}" href="{{ organization.get_absolute_url }}">
110+
{% trans "Projects" %}
111+
</a>
112+
<a class="item {{ teams_active }}" href="{% url "organization_team_list" organization.slug %}">
110113
{% trans "Teams" %}
111114
</a>
115+
<a class="item {{ members_active }}" href="{% url "organization_members" organization.slug %}">
116+
{% trans "Members" %}
117+
</a>
112118
{% if request.user|is_admin:organization %}
113119
<div class="right menu">
114120
<a class="item {{ edit_active }}" href="{% url "organization_edit" organization.slug %}">

readthedocsext/theme/templates/organizations/partials/invitation_list.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77

88
{% comment %}
99
For now, this include is a secondary listing on the owner/team member listing
10-
page. See the parent templates for an example of using this include. Note, I
11-
do not recommend repeating the pattern there, we should instead work towards
12-
either splitting these into dedicated UIs or combining them into a single model
13-
listing.
10+
page. See the parent templates for an example of using this include. Note,
11+
do not repeat the pattern there. Pages should not have multiple, competing
12+
model lists.
1413
{% endcomment %}
1514

1615
{% block top_menu %}
@@ -88,8 +87,9 @@
8887

8988
{% block list_item_meta_items %}
9089
<div class="item">
90+
{# Translators: this will read "Expires 2 weeks from now" #}
9191
{% blocktrans trimmed with until_expiration=object.expiration_date|naturaltime %}
92-
Expires in {{ until_expiration }}
92+
Expires {{ until_expiration }}
9393
{% endblocktrans %}
9494
</div>
9595
{% endblock list_item_meta_items %}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{% extends "organizations/partials/team_member_list.html" %}
2+
3+
{% load i18n %}
4+
{% load privacy_tags %}
5+
6+
{% block create_button %}
7+
{% endblock %}
8+
9+
{% block list_placeholder_header %}
10+
{% blocktrans trimmed %}
11+
No members found or no matches found for your filter criteria.
12+
{% endblocktrans %}
13+
{% endblock list_placeholder_header %}
14+
{% block list_placeholder_text %}
15+
{% if request.user|is_admin:organization %}
16+
<p class="inline">
17+
{% blocktrans trimmed %}
18+
To add members to your organization, invite new members to an existing organization team.
19+
{% endblocktrans %}
20+
</p>
21+
{% endif %}
22+
{% endblock list_placeholder_text %}

readthedocsext/theme/templates/organizations/partials/organization_list.html

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,7 @@
99
{% block view_binding %}data-bind="with: OrganizationListView()"{% endblock view_binding %}
1010

1111
{% block top_left_menu_items %}
12-
<script type="application/json" data-bind="jsonInit: config">
13-
{
14-
"api_url": "{% url "organizations-list" %}"
15-
}
16-
</script>
17-
18-
{# Separate UI for the version search selection field #}
19-
<div class="header item">Organization</div>
20-
<div class="active item">
21-
<div class="ui link search dropdown" data-bind="semanticui: {dropdown: filter_organization_config()}">
22-
<input type="hidden" name="organization" value="{{ filter.form.organization.value|default:"" }}">
23-
<div class="default text">{% trans "All organizations" %}</div>
24-
<i class="fa-solid fa-caret-down icon"></i>
25-
<div class="menu"></div>
26-
</div>
27-
</div>
28-
2912
{% include "includes/filters/form.html" with fields=filter.form %}
30-
3113
{% endblock top_left_menu_items %}
3214

3315
{% block create_button %}
@@ -95,10 +77,14 @@
9577

9678
{% block list_item_meta_items %}
9779
<a class="item" href="{{ object.get_absolute_url }}">
98-
{{ object.projects.count }} projects
80+
{% blocktrans trimmed with project_count=object.projects.count %}
81+
{{ project_count }} projects
82+
{% endblocktrans %}
9983
</a>
100-
<a class="item" href="{{ object.get_absolute_url }}">
101-
{{ object.teams.count }} teams
84+
<a class="item" href="{% url "organization_team_list" slug=object.slug %}">
85+
{% blocktrans trimmed with team_count=object.teams.count %}
86+
{{ team_count }} teams
87+
{% endblocktrans %}
10288
</a>
10389
{% endblock list_item_meta_items %}
10490

readthedocsext/theme/templates/organizations/partials/owners_list.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
{% block list_placeholder_icon_class %}fad fa-user-plus icon{% endblock %}
4141
{% block list_placeholder_header %}
4242
{% blocktrans trimmed %}
43-
No organization owners found or no matchse found for your filter criteria.
43+
No organization owners found or no matches found for your filter criteria.
4444
{% endblocktrans %}
4545
{% endblock list_placeholder_header %}
4646
{% block list_placeholder_text %}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
{% extends "projects/partials/project_list.html" %}
22

3+
{% block top_left_menu_items %}
4+
{# This partial is used a few times and ``filter`` isn't used in some views #}
5+
{% if filter %}
6+
{% include "includes/filters/form.html" with fields=filter.form %}
7+
{% endif %}
8+
{% endblock top_left_menu_items %}
9+
310
{# Don't show the create button on any of the org/team UIs #}
411
{% block create_button %}
512
{% endblock create_button %}

readthedocsext/theme/templates/organizations/partials/team_list.html

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
{% load privacy_tags %}
66

77
{% block top_left_menu_items %}
8+
<div data-bind="using: FilterView()">
9+
{% include "includes/filters/form.html" with fields=filter.form %}
10+
</div>
811
{% endblock top_left_menu_items %}
912

1013
{% block create_button %}
1114
{% if request.user|is_admin:organization %}
12-
<a class="ui green button" href="">
15+
<a class="ui green button" href="{% url "organization_team_add" organization.slug %}">
1316
{% trans "Add team" %}
1417
</a>
1518
{% endif %}
@@ -29,6 +32,24 @@
2932

3033
{% block list_item_right_buttons %}
3134
{% if request.user|is_admin:organization %}
35+
{# and request.user|org_owner:team %} #}
36+
<button class="ui dropdown button">
37+
<i class="fa-solid fa-ellipsis icon"></i>
38+
<div class="menu">
39+
<div class="header">{% trans "Admin" %}</div>
40+
<a class="item"
41+
href="{% url "organization_team_project_edit" organization.slug object.slug %}">
42+
<i class="fa-solid fa-plus icon"></i>
43+
{% trans "Manage projects" %}
44+
</a>
45+
<a class="item"
46+
href="{% url "organization_team_member_add" organization.slug object.slug %}">
47+
<i class="fa-duotone fa-user-plus icon"></i>
48+
{% trans "Invite member" %}
49+
</a>
50+
</div>
51+
</button>
52+
3253
{% blocktrans trimmed with team_name=object.name asvar label_configure %}
3354
Configure team {{ team_name }}
3455
{% endblocktrans %}
@@ -51,16 +72,19 @@
5172
{{ object.name }}
5273
</a>
5374
<div class="sub header">
75+
{{ object.get_access_display }}
76+
</div>
77+
{% endblock list_item_header %}
78+
79+
{% block list_item_meta_items %}
80+
<a class="item" href="{% url "organization_members" organization.slug %}?teams__slug={{ object.slug }}">
5481
{% blocktrans trimmed count object.members.count as member_count %}
5582
{{ member_count }} member
5683
{% plural %}
5784
{{ member_count }} members
5885
{% endblocktrans %}
59-
</div>
60-
{% endblock list_item_header %}
61-
62-
{% block list_item_meta_items %}
63-
<a class="item" href="{% url 'projects_dashboard' %}?team_slug=object.slug">
86+
</a>
87+
<a class="item" href="{% url "organization_detail" organization.slug %}?teams__slug={{ object.slug }}">
6488
{% blocktrans trimmed count object.projects.count as project_count %}
6589
{{ project_count }} project
6690
{% plural %}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
{% extends "includes/crud/table_list.html" %}
2+
3+
{% comment %}
4+
NOTE: If you are editing the UI in this template, you likely also want to
5+
update the templates for project maintainers and owner lists
6+
{% endcomment %}
7+
8+
{% load i18n %}
9+
{% load gravatar %}
10+
{% load privacy_tags %}
11+
12+
{% block top_left_menu_items %}
13+
{% if filter %}
14+
<div data-bind="using: FilterView()">
15+
{% include "includes/filters/form.html" with fields=filter.form %}
16+
</div>
17+
{% endif %}
18+
{% endblock top_left_menu_items %}
19+
20+
{% block create_button %}
21+
{% if request.user|is_admin:organization %}
22+
<a class="ui green button"
23+
href="{% url "organization_team_member_add" organization.slug team.slug %}">
24+
{% trans "Invite member" %}
25+
</a>
26+
{% endif %}
27+
{% endblock %}
28+
29+
{% block list_placeholder_icon_class %}fad fa-user-plus icon{% endblock %}
30+
{% block list_placeholder_header %}
31+
{% blocktrans trimmed %}
32+
No team members match your filter criteria.
33+
{% endblocktrans %}
34+
{% endblock list_placeholder_header %}
35+
{% block list_placeholder_text %}
36+
{% if request.user|is_admin:organization %}
37+
<div class="ui primary button">{% trans "Invite member" %}</div>
38+
{% endif %}
39+
{% endblock list_placeholder_text %}
40+
41+
{% block list_item_right_buttons %}
42+
{% endblock list_item_right_buttons %}
43+
44+
{% block list_item_image %}
45+
<img class="ui rounded square image" src="{% gravatar_url object.email 64 %}">
46+
{% endblock list_item_image %}
47+
48+
{% block list_item_header %}
49+
{% with full_name=object.get_full_name %}
50+
<a href="{% url "profiles_profile_detail" object.username %}">
51+
52+
{% if full_name %}
53+
{{ full_name }}
54+
{% else %}
55+
{{ object.username }}
56+
{% endif %}
57+
58+
{% comment %}
59+
This would be a great place to use labels to explain the user's access
60+
level. We'd need a property on the User model to get the highest level
61+
of access through one of the available teams/organization owners. Use a
62+
label element to show the labels, similar to the version listing and
63+
other listing views.
64+
{% endcomment %}
65+
</a>
66+
{% if full_name %}
67+
<div class="sub header">
68+
{{ object.username }}
69+
</div>
70+
{% endif %}
71+
{% endwith %}
72+
{% endblock list_item_header %}
73+
74+
{% block list_item_meta_items %}
75+
{% endblock list_item_meta_items %}
76+
77+
{% block list_item_extra %}
78+
{% endblock list_item_extra %}

readthedocsext/theme/templates/organizations/settings/owners_edit.html

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,33 @@
1111
{% include "organizations/partials/owners_list.html" with objects=owners %}
1212

1313
{% comment %}
14+
TODO move owner/team member invitations to a dedicated view.
15+
16+
Do not reuse this pattern, it is a bad pattern.
17+
1418
This is a bit of a hack to avoid manipulating the view to work with
1519
multiple querysets on two separate models. On the owners/team member views,
1620
there will also be a listing of invitations (the `Invitation` model). But,
17-
the invitiation list will not respond to pagination. This at least allows
18-
the owner listing to paginate and not affect the invitation list. It also
19-
means that any number of invitation over the pagination limit probably won't
20-
show or at least will clog up the UI.
21-
22-
Ideally, both OrganizationOwner and Invitation model queryset would be
23-
joined into a single queryset listing, but for now this will use a separate
24-
element to list the outstanding invitations. I would not recommend repeating
25-
this pattern outside invitations.
21+
the invitiation list will not respond to pagination without altering the
22+
view to support multi model pagination.
23+
24+
This means that invitations could clog up the UI, as they won't be paginated,
25+
and the invitations will to stuffed below the view model listing, so really
26+
hard to find.
27+
28+
Ideally, these should either be separate views entirely, or both
29+
OrganizationOwner and Invitation model queryset would be joined into a
30+
single queryset listing.
2631
{% endcomment %}
27-
{% if invitations.exists %}
28-
<h3 class="ui small header">
29-
{% trans "Pending invitations" %}
30-
<span class="ui circular small label">
31-
{{ invitations.count }}
32-
</span>
33-
</h3>
34-
{% include "organizations/partials/invitation_list.html" with objects=invitations skip_pagination=True %}
35-
{% endif %}
32+
{% block team_invitations %}
33+
{% if invitations.exists %}
34+
<h2 class="ui small header">
35+
{% trans "Pending invitations" %}
36+
<span class="ui circular small label">
37+
{{ invitations.count }}
38+
</span>
39+
</h2>
40+
{% include "organizations/partials/invitation_list.html" with objects=invitations skip_pagination=True %}
41+
{% endif %}
42+
{% endblock team_invitations %}
3643
{% endblock organization_edit_content %}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
team_form.html

0 commit comments

Comments
 (0)