[RFC PATCH 10/19] templates: Convert user profile view
Stephen Finucane
stephen at that.guru
Thu Aug 12 07:36:56 AEST 2021
This is our first "full page", in that we have a navbar. A large number
of TODOs are left here as we're simply changing templates rather than
updating views, but those gaps will be closed in a future change.
Signed-off-by: Stephen Finucane <stephen at that.guru>
---
patchwork/templates/patchwork/login.html | 5 +-
patchwork/templates/patchwork/profile.html | 432 ++++++++++++------
templates/base2.html | 82 ++++
.../registration/password_reset_confirm.html | 4 +
.../registration/password_reset_done.html | 4 +
.../registration/password_reset_form.html | 4 +
6 files changed, 395 insertions(+), 136 deletions(-)
diff --git patchwork/templates/patchwork/login.html patchwork/templates/patchwork/login.html
index ef609f1f..b8ab462c 100644
--- patchwork/templates/patchwork/login.html
+++ patchwork/templates/patchwork/login.html
@@ -2,8 +2,7 @@
{% block title %}Sign in to Patchwork{% endblock %}
-{% block headers %}
-{% endblock %}
+{% block navigation %}{% endblock %}
{% block body %}
<section class="hero is-primary is-fullheight">
@@ -75,3 +74,5 @@ for (var i = 0; i < btns.length; i++) {
}
</script>
{% endblock %}
+
+{% block footer %}{% endblock %}
diff --git patchwork/templates/patchwork/profile.html patchwork/templates/patchwork/profile.html
index 552dde47..7a0b54fe 100644
--- patchwork/templates/patchwork/profile.html
+++ patchwork/templates/patchwork/profile.html
@@ -1,173 +1,337 @@
-{% extends "base.html" %}
+{% extends "base2.html" %}
{% block title %}{{ user.username }}{% endblock %}
-{% block heading %}Your Profile{% endblock %}
{% block body %}
-<h1>Your Profile</h1>
+<div class="container" style="margin-top: 1rem;">
+ <div class="columns">
+ <div class="column is-3">
+ <aside class="menu">
+ <p class="menu-label">
+ Overview
+ </p>
+ <ul class="menu-list">
+ <li><a href="#projects">Projects</a></li>
+ <li><a href="#bundles">Bundles</a></li>
+ <li><a href="#todo">Todo List</a></li>
+ </ul>
+ <p class="menu-label">
+ Settings
+ </p>
+ <ul class="menu-list">
+ <li><a href="#profile">Profile</a></li>
+ <li><a href="#linked-emails">Linked emails</a></li>
+ <li><a href="#profile-settings">Profile settings</a></li>
+ <li><a href="#security">Security</a></li>
+ </ul>
+ </aside>
+ </div>
+ <div class="column is-9">
+ <h1 id="overview" class="title">
+ <a href="#overview" title="Permalink to this section"></a>
+ Overview
+ </h1>
+
+ <section class="block">
+ <h2 id="projects" class="title is-4">
+ <a href="#projects" title="Permalink to this section">#</a>
+ Projects
+ </h2>
{% if user.profile.maintainer_projects.count %}
-<p>
- Maintainer of
+ <p>
+ Maintainer of
{% for project in user.profile.maintainer_projects.all %}
- <a href="{% url 'patch-list' project_id=project.linkname %}">{{ project.linkname }}</a>{% if not forloop.last %},{% endif %}
+ <a href="{% url 'patch-list' project_id=project.linkname %}">{{ project.linkname }}</a>{% if not forloop.last %},{% endif %}
{% endfor %}.
-</p>
+ </p>
{% endif %}
-
{% if user.profile.contributor_projects.count %}
-<p>
- Contributor to
+ <p>
+ Contributor to
{% for project in user.profile.contributor_projects.all %}
- <a href="{% url 'patch-list' project_id=project.linkname %}">{{ project.linkname }}</a>{% if not forloop.last %},{% endif %}
+ <a href="{% url 'patch-list' project_id=project.linkname %}">{{ project.linkname }}</a>{% if not forloop.last %},{% endif %}
{% endfor %}.
-</p>
+ </p>
{% endif %}
+ </section>
-<div class="leftcol">
- <div class="box">
- <h2>Todo</h2>
- <p>
- Your <a href="{% url 'user-todos' %}">todo list</a> contains patches that
- have been delegated to you.
+ <section class="block">
+ <h2 id="bundles" class="title is-4">
+ <a href="#bundles" title="Permalink to this section">#</a>
+ Bundles
+ </h2>
+{% if bundles %}
+ <p>You have the following bundle{{ bundles|length|pluralize }}:</p>
+ <ul>
+{% for bundle in bundles %}
+ <li><a href="{{ bundle.get_absolute_url }}">{{ bundle.name }}</a></li>
+{% endfor %}
+ </ul>
+ <p>
+ Visit the <a href="{% url 'user-bundles' %}">bundles page</a> to manage your bundles.
+ </p>
+{% else %}
+ <p>You have no bundles.</p>
+{% endif %}
+ </section>
+
+ <section class="block">
+ <h2 id="todo" class="title is-4">
+ <a href="#todo" title="Permalink to this section">#</a>
+ Todo List
+ </h2>
+ <p>
+ Your <a href="{% url 'user-todos' %}">todo list</a> contains patches that
+ have been delegated to you.
+ </p>
+ <p>
{% if user.profile.n_todo_patches %}
- Your have {{ user.profile.n_todo_patches }}
- patch{{ user.profile.n_todo_patches|pluralize:"es" }} in your todo list.
+ Your have {{ user.profile.n_todo_patches }}
+ patch{{ user.profile.n_todo_patches|pluralize:"es" }} in your todo list.
{% else %}
- You have no patches in your todo list at present.
+ You have no patches in your todo list at present.
{% endif %}
- </p>
- </div>
+ </p>
+ </section>
- <div class="box">
- <h2>Linked email addresses</h2>
- <p>
- The following email addresses are associated with this Patchwork account.
- Adding alternative addresses allows Patchwork to group contributions that
- you have made under different addresses.
- </p>
- <p>
- The "notify?" column allows you to opt-in or opt-out of automated
- Patchwork notification emails. Setting it to "no" will disable automated
- notifications for that address.
- </p>
- <p>
- Adding a new email address will send a confirmation email to that address.
- </p>
- <table class="vertical">
- <tr>
- <th>email</th>
- <th>action</th>
- <th>notify?</th>
- </tr>
+ <h1 id="settings" class="title">
+ <a href="#settings" title="Permalink to this section"></a>
+ Settings
+ </h1>
+
+{# TODO: Add view to enable this #}
+ <section class="block">
+ <h2 id="profile" class="title is-4">
+ <a href="#profile" title="Permalink to this section">#</a>
+ Profile
+ </h2>
+ <form method="post">
+ {% csrf_token %}
+ <div class="field">
+ <label for="id_username" class="label">
+ Username
+ </label>
+ <div class="control">
+ <input id="id_username" type="text" name="name" class="input" value="{{ user.username }}" disabled>
+ </div>
+ </div>
+ <div class="field">
+ <label for="id_first_name" class="label">
+ First name
+ </label>
+ <div class="control">
+ <input id="id_first_name" type="text" name="first_name" class="input" autocomplete="given-name" value="{{ user.first_name }}">
+ </div>
+ </div>
+ <div class="field">
+ <label for="id_last_name" class="label">
+ Last name
+ </label>
+ <div class="control">
+ <input id="id_last_name" type="text" name="last_name" class="input" autocomplete="family-name" value="{{ user.last_name }}">
+ </div>
+ </div>
+ <div class="control">
+ <button class="button is-primary is-disabled">Save</button>
+ </div>
+ </form>
+ </section>
+
+ <section class="block">
+ <h2 id="linked-emails" class="title is-4">
+ <a href="#linked-emails" title="Permalink to this section">#</a>
+ Linked emails
+ </h2>
{% for email in linked_emails %}
- <tr>
- <td>{{ email.email }}</td>
- <td>
+ <div class="card">
+ <div class="card-content">
+ <div class="columns">
+ <div class="column">
+ <span>{{ email.email }}</span>
+{% if user.email == email.email %}
+ <span class="tag is-primary is-medium">Primary</span>
+{% endif %}
+ </div>
{% if user.email != email.email %}
- <form action="{% url 'user-unlink' person_id=email.id %}" method="post">
- {% csrf_token %}
- <input type="submit" value="Unlink"/>
- </form>
+ <div class="column is-narrow">
+ <form method="post" action="{% url 'user-unlink' person_id=email.id %}">
+ {% csrf_token %}
+ <button class="button is-danger">Unlink</button>
+ </form>
+ </div>
+{# TODO: Add view to enable this #}
+ <div class="column is-narrow">
+ <form method="post">
+ {% csrf_token %}
+ <button class="button is-info">Make primary</button>
+ </form>
+ </div>
{% endif %}
- </td>
- <td>
+ <div class="column is-narrow">
{% if email.is_optout %}
- <form method="post" action="{% url 'mail-optin' %}">
- {% csrf_token %}
- No,
- <input type="hidden" name="email" value="{{ email.email }}"/>
- <input type="submit" value="Opt-in"/>
- </form>
+ <form method="post" action="{% url 'mail-optin' %}">
+ {% csrf_token %}
+ <input type="hidden" name="email" value="{{ email.email }}"/>
+ <button class="button is-info is-right">Opt-in</button>
+ </form>
{% else %}
- <form method="post" action="{% url 'mail-optout' %}">
- {% csrf_token %}
- Yes,
- <input type="hidden" name="email" value="{{ email.email }}"/>
- <input type="submit" value="Opt-out"/>
- </form>
+ <form method="post" action="{% url 'mail-optout' %}">
+ {% csrf_token %}
+ <input type="hidden" name="email" value="{{ email.email }}"/>
+ <button class="button is-info">Opt-out</button>
+ </form>
{% endif %}
- </td>
- </tr>
+ </div>
+ </div>
+ </div>
+ </div>
{% endfor %}
- <tr>
- <td colspan="3">
- <form action="{% url 'user-link' %}" method="post">
+ <div class="block"></div>
+ <div class="block">
+ <form class="block" method="post" action="{% url 'user-link' %}">
{% csrf_token %}
- {{ linkform.email }}
- <input type="submit" value="Add"/>
+ <label for="id_email" class="label">
+ Add email address
+ </label>
+ <div class="field is-grouped">
+ <div class="control">
+ <input id="id_email" type="email" name="email" placeholder="e.g. bobsmith at example.com" class="input" required>
+ </div>
+ <div class="control">
+ <button class="button is-info">
+ Add email
+ </button>
+ </div>
+ </div>
</form>
- </td>
- </tr>
- </table>
- </div>
-</div>
-
-<div class="rightcol">
- <div class="box">
- <h2>Bundles</h2>
-{% if bundles %}
- <p>You have the following bundle{{ bundles|length|pluralize }}:</p>
- <ul>
-{% for bundle in bundles %}
- <li><a href="{{ bundle.get_absolute_url }}">{{ bundle.name }}</a></li>
-{% endfor %}
- </ul>
- <p>Visit the <a href="{%url 'user-bundles' %}">bundles page</a> to manage your bundles.</p>
-{% else %}
- <p>You have no bundles.</p>
-{% endif %}
- </div>
+ </div>
+ </section>
- <div class="box">
- <h2>Settings</h2>
+ <section class="block">
+ <h2 id="profile-settings" class="title is-4">
+ <a href="#profile-settings" title="Permalink to this section">#</a>
+ Profile settings
+ </h2>
+ <form class="block" method="post">
+ {% csrf_token %}
+ <div class="field">
+ <label for="id_items_per_page" class="label">
+ Items per page
+ </label>
+ <div class="control">
+ <input id="id_items_per_page" type="number" name="items_per_page" class="input" value="{{ user.profile.items_per_page }}" required>
+ <p class="help">Number of items to display per page</p>
+ </div>
+ </div>
+ <div class="field">
+ <p class="label">
+ Show patch IDs
+ </p>
+ <div class="control">
+ <label class="radio">
+ <input type="radio" name="show_ids">
+ Yes
+ </label>
+ <label class="radio">
+ <input type="radio" name="show_ids">
+ No
+ </label>
+ <p class="help">Show click-to-copy patch IDs in the list view</p>
+ </div>
+ </div>
+ <div class="control">
+ <button class="button is-primary is-disabled">Update settings</button>
+ </div>
+ </form>
+ </section>
- <form method="post">
- {% csrf_token %}
- <table class="form">
- {{ profileform }}
- <tr>
- <td></td>
- <td><input type="submit" value="Apply"/></td>
- </tr>
- </table>
- </form>
- </div>
-
- <div class="box">
- <h2>Authentication</h2>
-
- <table class="form">
- <tr>
- <th>Password:</th>
- <td><a href="{% url 'password_change' %}">Change password</a>
- </tr>
-{% if rest_api_enabled %}
- <tr>
- <th>API Token:</th>
- <td>
-{% if api_token %}
- <input id="token" style="width: 25em;" readonly value="{{ api_token }}">
- <button type="button" class="btn-copy" title="Copy to clipboard" data-clipboard-target="#token">Copy</button>
-{% endif %}
- </td>
- <tr>
- <th></th>
- <td>
+ <section class="block">
+ <h2 id="security" class="title is-4">
+ <a href="#security" title="Permalink to this section">#</a>
+ Security
+ </h2>
+ <form class="block" method="post" action="{% url 'password_change' %}">
+ {% csrf_token %}
+ <div class="field">
+ <label for="id_old_password" class="label">
+ Current password
+ </label>
+ <div class="control">
+ <input id="id_old_password" type="password" name="old_password" class="input" required>
+ </div>
+ </div>
+ <div class="field">
+ <label for="id_new_password1" class="label">
+ New password
+ </label>
+ <div class="control">
+ <input id="id_new_password1" type="password" name="new_password1" class="input" required>
+ </div>
+ </div>
+ <div class="field">
+ <label for="id_new_password2" class="label">
+ Confirm password
+ </label>
+ <div class="control">
+ <input id="id_new_password2" type="password" name="new_password2" class="input" required>
+ </div>
+ </div>
+ <div class="control">
+ <button class="button is-primary is-disabled">Update password</button>
+ </div>
+ </form>
+ <div class="block">
+ <label for="id_api_token" class="label">
+ API token
+ </label>
+ <div class="field has-addons">
+ <div class="control is-expanded">
+ <input id="id_api_token" type="text" name="name" class="input" value="{{ api_token|default_if_none:'' }}" disabled>
+ </div>
+{# TODO: wire this up #}
+ <div class="control">
+ <button class="button is-info">
+ Copy
+ </button>
+ </div>
+ </div>
<form method="post" action="{% url 'generate_token' %}">
{% csrf_token %}
+ <div class="control">
{% if api_token %}
- <input type="submit" value="Regenerate token"/>
+ <button class="button is-primary">Regenerate token</button>
{% else %}
- <input type="submit" value="Generate token"/>
+ <button class="button is-primary">Generate token</button>
{% endif %}
+ </div>
</form>
- </td>
- </tr>
-{% endif %}
- </table>
+ </div>
+ </section>
+ </div>
</div>
</div>
-<p style="clear: both"></p>
+<script>
+document.addEventListener('DOMContentLoaded', () => {
+ // Get all "navbar-burger" elements
+ const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
+
+ // Check if there are any navbar burgers
+ if ($navbarBurgers.length > 0) {
+ // Add a click event on each of them
+ $navbarBurgers.forEach( el => {
+ el.addEventListener('click', () => {
+ // Get the target from the "data-target" attribute
+ const target = el.dataset.target;
+ const $target = document.getElementById(target);
+
+ // Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
+ el.classList.toggle('is-active');
+ $target.classList.toggle('is-active');
+ });
+ });
+ }
+});
+</script>
{% endblock %}
diff --git templates/base2.html templates/base2.html
index ac6b43bc..6380c37b 100644
--- templates/base2.html
+++ templates/base2.html
@@ -10,6 +10,88 @@
{% block headers %}{% endblock %}
</head>
<body>
+{% block navigation %}
+ <nav class="navbar is-white" role="navigation" aria-label="main navigation">
+ <div class="container">
+ <div class="navbar-brand">
+ <a class="navbar-item brand-text" href="/">Patchwork</a>
+ <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navMenu">
+ <span aria-hidden="true"></span>
+ <span aria-hidden="true"></span>
+ <span aria-hidden="true"></span>
+ </a>
+ </div>
+ <div id="navMenu" class="navbar-menu">
+ <div class="navbar-start">
+ <a class="navbar-item" href="{% url 'user-todos' %}">Todo List</a>
+ <a class="navbar-item" href="{% url 'user-bundles' %}">Bundles</a>
+ <a class="navbar-item" href="{% url 'project-list' %}">Projects</a>
+ </div>
+ <div class="navbar-end">
+{% if user.is_authenticated %}
+ <div class="navbar-item has-dropdown is-hoverable">
+ <a class="navbar-link">
+ <span class="icon-text">
+ <span class="icon">
+ <i class="fa fa-user"></i>
+ </span>
+ <span>{{ user.username }}</span>
+ </span>
+ </a>
+ <div class="navbar-dropdown is-right">
+ <a class="navbar-item" href="{% url 'user-todos' %}">
+ Todo List
+ </a>
+ <a class="navbar-item" href="{% url 'user-bundles' %}">
+ Bundles
+ </a>
+ <a class="navbar-item" href="{% url 'user-profile' %}">
+ Profile
+ </a>
+{% if user.is_staff %}
+ <hr class="navbar-divider">
+ <a class="navbar-item" href="{% url 'admin:index' %}">
+ Patchwork Settings
+ </a>
+{% endif %}
+ <hr class="navbar-divider">
+ <a class="navbar-item" href="{% url 'auth_logout' %}">
+ Sign out
+ </a>
+ </div>
+ </div>
+{% else %}
+ <div class="navbar-item">
+ <div class="buttons">
+ <a href="{% url 'user-register' %}" class="button is-primary">
+ <strong>Sign up</strong>
+ </a>
+ <a href="{% url 'auth_login' %}" class="button is-light">
+ Log in
+ </a>
+ </div>
+ </div>
+{% endif %}
+ </div>
+ </div>
+ </div>
+ </nav>
+{% endblock %}
{% block body %}{% endblock %}
+{% block footer %}
+ <div class="block"></div>
+
+ <footer class="footer">
+ <div class="content has-text-centered">
+ <p>
+ <a href="https://github.com/getpatchwork/patchwork/">Patchwork patch tracking system</a>
+ •
+ Version {{ version }}
+ •
+ <a href="{% url 'about' %}">About Patchwork</a>
+ </p>
+ </div>
+ </footer>
+{% endblock %}
</body>
</html>
diff --git templates/registration/password_reset_confirm.html templates/registration/password_reset_confirm.html
index 1c91eb1b..90a7e136 100644
--- templates/registration/password_reset_confirm.html
+++ templates/registration/password_reset_confirm.html
@@ -3,6 +3,8 @@
{% block title %}Password reset confirmation{% endblock %}
{% block heading %}Password reset confirmation{% endblock %}
+{% block navigation %}{% endblock %}
+
{% block body %}
<section class="hero is-primary is-fullheight">
<div class="hero-body">
@@ -75,3 +77,5 @@
</div>
</section>
{% endblock %}
+
+{% block footer %}{% endblock %}
diff --git templates/registration/password_reset_done.html templates/registration/password_reset_done.html
index ebd38e68..65b65e77 100644
--- templates/registration/password_reset_done.html
+++ templates/registration/password_reset_done.html
@@ -2,6 +2,8 @@
{% block title %}Password reset email sent!{% endblock %}
+{% block navigation %}{% endblock %}
+
{% block body %}
<section class="hero is-primary is-fullheight">
<div class="hero-body">
@@ -26,3 +28,5 @@
</div>
</section>
{% endblock %}
+
+{% block footer %}{% endblock %}
diff --git templates/registration/password_reset_form.html templates/registration/password_reset_form.html
index 431b4696..4f0c3a6a 100644
--- templates/registration/password_reset_form.html
+++ templates/registration/password_reset_form.html
@@ -2,6 +2,8 @@
{% block title %}Forgot your password?{% endblock %}
+{% block navigation %}{% endblock %}
+
{% block body %}
<section class="hero is-primary is-fullheight">
<div class="hero-body">
@@ -46,3 +48,5 @@
</div>
</section>
{% endblock %}
+
+{% block footer %}{% endblock %}
--
2.31.1
More information about the Patchwork
mailing list