{% extends 'base.html' %}
|
{% load static %}
|
|
{% block head %}
|
<link rel="stylesheet" href="{{ settings.HOSTNAME }}{% static 'css/users.css' %}">
|
{% endblock %}
|
|
{% block divider %}
|
{% endblock %}
|
|
{% block frontend_settings %}
|
{
|
breadcrumbs: [
|
{
|
title: "Account & Settings"
|
}
|
],
|
}
|
{% endblock %}
|
|
{% block content %}
|
<script>
|
// Take closest form (or form by id) and submit it,
|
// optional: prevent page refresh if done passed as function
|
function smart_submit(done, form_id) {
|
// use form id or take closest form to the event target
|
var f = typeof form_id === 'string' ? $(form_id) : $(event.target).closest('form');
|
console.log('smart_submit found form to use', f);
|
|
if (typeof f === 'undefined') {
|
console.log('smart_submit event target', event.target);
|
alert("Closest form not found for smart_submit")
|
}
|
console.log('YYYYYYY' + $(f).serialize());
|
var params = {
|
url: $(f).attr('action'),
|
type: $(f).attr('method'),
|
data: $(f).serialize(),
|
|
error: function (result, textStatus, errorThrown) {
|
console.log('smart_submit ajax error', result);
|
|
// call done function if it's defined and ignore all the rest
|
if (typeof done === "function") {
|
done(result)
|
}
|
// default error handle
|
else {
|
if (typeof result.responseText !== 'undefined') {
|
alert("Error: " + message_from_response(result));
|
} else {
|
alert("Request error: " + errorThrown);
|
}
|
window.location.reload();
|
}
|
},
|
|
success: function (data, textStatus, result) {
|
// call done function if it's defined and ignore all the rest
|
if (typeof done === "function") {
|
done(result)
|
}
|
// default success handle
|
else {
|
window.location.reload();
|
}
|
}
|
};
|
|
console.log('smart_submit ajax params', params);
|
$.ajax(params);
|
event.preventDefault();
|
return false;
|
}
|
|
function message_from_response(result) {
|
console.log(result);
|
|
// result is dict
|
if (result.hasOwnProperty('detail') && result.detail)
|
return result.detail;
|
|
// result is object from XHR, check responseText first, it is always presented
|
if (!result.responseText)
|
return 'Critical error on server';
|
|
// grab responseJSON detail
|
else if (result.responseJSON && result.responseJSON.hasOwnProperty('detail'))
|
return result.responseJSON['detail'];
|
|
// something strange inside of responseJSON
|
else if (result.responseJSON)
|
return JSON.stringify(result.responseJSON);
|
|
else
|
return JSON.stringify(result)
|
}
|
</script>
|
|
<div class="full_content">
|
<div class="account-page">
|
|
<div class="forms">
|
<form action="{% url 'user-detail' pk=user.pk %}" method="patch" class="user__info">
|
<input type="hidden" name="_method" value="patch"/>
|
<header>Your Avatar</header>
|
<div class="user-pic {{ user.avatar|yesno:'can_delete,can_upload' }}">
|
<div class="userpic userpic--big">
|
{% if user.avatar %}
|
<img src="{{user.avatar_url}}" alt="User photo" width="92" />
|
{% endif %}
|
|
{% if user.get_initials %}
|
<span>{{user.get_initials}}</span>
|
{% else %}
|
<span>{{user.username}}</span>
|
{% endif %}
|
</div>
|
|
<div class="controls">
|
<button class="lsf-button-ls lsf-button-ls_look_danger" name="delete-avatar" type="button">
|
Delete
|
</button>
|
|
<input class="file-input" type="file" name="avatar" value="Choose"
|
accept="image/png, image/jpeg, image/jpg"/>
|
</div>
|
</div>
|
<header>Personal Information</header>
|
<ul>
|
<li class="field">
|
<label for="email">E-mail</label>
|
<input type="text" class="lsf-input-ls" id="email" name="email" value="{{user.email}}" disabled />
|
</li>
|
<li class="field">
|
<label for="first_name">First Name</label>
|
<input type="text" class="lsf-input-ls" id="first_name" name="first_name" value="{{user.first_name}}" />
|
</li>
|
<li class="field">
|
<label for="last_name">Last Name</label>
|
<input type="text" class="lsf-input-ls" id="last_name" name="last_name" value="{{user.last_name}}" />
|
</li>
|
<li class="field">
|
<label for="phone">Phone</label>
|
<input type="text" class="lsf-input-ls" id="phone" name="phone" value="{{user.phone}}" />
|
<!-- <span>We'll send you sms with code if you change your number</span>
|
<span class="error">Incorrect phone number!</span> -->
|
</li>
|
</ul>
|
<div class="user-some-actions">
|
|
|
</div>
|
<footer>
|
<p class="secondary">Registered {{ user.date_joined|date:"M j, Y" }}, user ID {{ user.id }}</p>
|
<button class="lsf-button-ls lsf-button-ls_look_primary" onclick="smart_submit()">Save</button>
|
</footer>
|
</form>
|
|
<!-- Token -->
|
<form action="" class="access_token__info">
|
<header>Access Token</header>
|
<div class="field field--wide">
|
<label for="access_token">Use this token to authenticate with our API:</label>
|
<input id="access_token" class="lsf-input-ls" name="access_token" type="text" value="{{token}}" readonly />
|
<p class="actions">
|
<button type="button" class="blinking-status lsf-button-ls" data-copy="access_token">Copy</button>
|
<button type="button" class="blinking-status lsf-button-ls" name="renew">Renew</button>
|
</p>
|
</div>
|
<!-- Example -->
|
<div class="field field--wide">
|
<label for="example_fetch">Example fetch projects data:</label>
|
<textarea id="example_fetch" class="example_code ls-textarea" type="text" readonly
|
style="height: 92px; font-size: 14px; padding: 9px 16px">
|
{% if settings.HOSTNAME %}
|
curl -X GET {{ settings.HOSTNAME }}/api/projects/ -H 'Authorization: Token {{token}}'
|
{% else %}
|
curl -X GET {{ request.scheme }}://{{ request.get_host }}/api/projects/ -H 'Authorization: Token {{token}}'
|
{% endif %}
|
</textarea>
|
<p class="actions">
|
<button type="button" class="blinking-status lsf-button-ls" data-copy="example_fetch">Copy</button>
|
|
{% block api_docs %}
|
<a
|
class="lsf-button-ls"
|
href="https://labelstud.io/guide/api.html"
|
target="_blank"
|
>
|
Documentation
|
</a>
|
{% endblock %}
|
</p>
|
</div>
|
</form>
|
|
|
<!-- Organization -->
|
<form action="" class="organization block-info" id="organization">
|
<header>
|
{{ user.active_organization.title }}
|
<br>
|
<sub>Your active organization</sub>
|
</header>
|
|
<table>
|
{% with user.get_pretty_role as role %}
|
{% if role %}
|
<tr><td>Your role</td><td>{{ user.get_pretty_role }}</td></tr>
|
{% endif %}
|
{% endwith %}
|
<tr><td>Annotations completed by you</td><td>{{ user.active_organization_annotations.count }}</td></tr>
|
<tr><td>Projects contributed by you</td><td>{{ user.active_organization_contributed_project_number }}</td></tr>
|
<tr><td></td><td></td></tr>
|
<tr><td style="min-width: 75px">Organization ID</td><td>{{ user.active_organization.id }}</td></tr>
|
<tr><td>Organization owner</td><td>{{ user.active_organization.created_by }}</td></tr>
|
<tr><td>Organization created at</td><td>{{ user.active_organization.created_at }}</td></tr>
|
</table>
|
|
</form>
|
|
<!-- Notifications -->
|
{% block notifications %}
|
<form action="{% url 'user-detail' pk=user.pk %}?update-notifications=1" method="patch" class="notifications block-info" id="notifications">
|
<header>
|
Notifications
|
<br>
|
<sub>Email and other notifications</sub>
|
</header>
|
|
<table>
|
<tr><td style="{% if user.allow_newsletters is None %}border: 1px red solid; border-radius: 5px{% endif %}">
|
|
<input name="allow_newsletters" type="hidden"
|
value="{% if user.allow_newsletters is None %}true{% else %}{{ user.allow_newsletters|yesno:"false,true" }}{% endif %}">
|
|
<input name="allow_newsletters_visual" id="allow_newsletters_visual" type="checkbox"
|
style="width: auto;"
|
{% if user.allow_newsletters %}checked="true"{% endif %}
|
onclick="smart_submit()">
|
|
<label for="allow_newsletters_visual" style="display: inline-block; cursor: pointer; margin-top: -10px">
|
Get the latest news & tips from Heidi
|
<img src="{{ settings.HOSTNAME }}{% static 'images/heidi.png' %}" alt="Heidi"
|
width="25" style="display: inline; margin: 0; position: relative; top: 5px; left: 0">
|
</label>
|
|
</td></tr>
|
</table>
|
|
</form>
|
{% endblock %}
|
</div>
|
|
|
|
</div>
|
|
<script>
|
(() => {
|
{% if settings.HOSTNAME %}
|
const hostname = '{{ settings.HOSTNAME }}';
|
{% else %}
|
const hostname = '{{ request.scheme }}://{{ request.get_host }}';
|
{% endif %}
|
|
document.querySelectorAll('[data-copy]').forEach(button => {
|
button.onclick = e => {
|
const input = document.getElementById(e.target.dataset.copy);
|
input.select();
|
document.execCommand("copy");
|
input.setSelectionRange(0, 0);
|
input.blur();
|
button.classList.add('blink');
|
setTimeout(() => button.classList.remove('blink'))
|
}
|
});
|
|
document.querySelector('[name=renew]').onclick = e => {
|
const button = e.target;
|
const input = document.getElementById("access_token");
|
const example = document.getElementById("example_fetch");
|
|
fetch("{% url 'current-user-reset-token' %}", { method: "POST" })
|
.then(res => res.json())
|
.then(res => {
|
input.value = res.token;
|
example.value = `curl -X GET ${hostname}/api/projects/ -H 'Authorization: Token ${res.token}'`
|
button.classList.add('blink');
|
setTimeout(() => button.classList.remove('blink'))
|
});
|
};
|
|
$('[name=avatar]').on('change', async (e) => {
|
const formData = new FormData;
|
|
formData.append(e.target.name, e.target.files[0]);
|
|
try {
|
const response = await fetch("{% url 'user-avatar' pk=user.pk %}", {
|
method: "post",
|
body: formData
|
});
|
|
if (!response.ok) {
|
handleResponseError(response)
|
} else {
|
updateAvatar(true)
|
}
|
} catch (err) {
|
console.error(err)
|
}
|
});
|
|
$('[name=delete-avatar]').on('click', async (e) => {
|
try {
|
const response = await fetch("{% url 'user-avatar' pk=user.pk %}", {
|
method: "delete"
|
})
|
|
if (!response.ok) {
|
handleResponseError(response)
|
} else {
|
updateAvatar(false)
|
}
|
} catch (err) {
|
console.err(err)
|
}
|
})
|
|
/**
|
* @param {Response} response
|
*/
|
const handleResponseError = (response) => {
|
response.json().then(data => {
|
alert(message_from_response(data));
|
})
|
}
|
|
const updateAvatar = async (setAvatar = true) => {
|
if (setAvatar) {
|
const response = await fetch("{% url 'current-user-whoami' %}")
|
|
if (response.ok) {
|
const {avatar} = await response.json()
|
const userpic = document.querySelector('.userpic')
|
|
let userpicImage = userpic.querySelector('img')
|
|
if (!userpicImage) {
|
userpicImage = document.createElement('img')
|
userpic.insertBefore(userpicImage, userpic.firstChild);
|
}
|
|
userpicImage.src = avatar
|
|
const userpicRoot = document.querySelector('.user-pic');
|
userpicRoot.classList.remove('can_delete', 'can_upload')
|
userpicRoot.classList.add('can_delete')
|
}
|
} else {
|
const userpic = document.querySelector('.user-pic')
|
const userpicImage = userpic.querySelector('img')
|
if (userpicImage) userpicImage.remove();
|
|
userpic.classList.remove('can_delete', 'can_upload')
|
userpic.classList.add('can_upload')
|
}
|
}
|
})();
|
</script>
|
</div>
|
|
{% endblock %}
|