Admin pages
This commit is contained in:
Коммит
7aac1059da
|
@ -47,5 +47,7 @@
|
|||
"sw-precache": "^5.2.1",
|
||||
"sw-toolbox": "^3.6.0"
|
||||
},
|
||||
"dependencies": {}
|
||||
"dependencies": {
|
||||
"lit-element": "^2.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
import {LitElement, html} from 'https://unpkg.com/@polymer/lit-element@latest/lit-element.js?module';
|
||||
|
||||
class ChromedashUserlist extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
actionPath: {type: String},
|
||||
users: {type: Array},
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.users = [];
|
||||
}
|
||||
|
||||
addUser(user) {
|
||||
this.users.splice(0, 0, user);
|
||||
this.users = this.users.slice(0); // Refresh the list
|
||||
}
|
||||
|
||||
removeUser(idx) {
|
||||
this.users.splice(idx, 1);
|
||||
this.users = this.users.slice(0); // Refresh the list
|
||||
}
|
||||
|
||||
ajaxSubmit(e) {
|
||||
e.preventDefault();
|
||||
const formEl = this.shadowRoot.querySelector('form');
|
||||
|
||||
if (formEl.checkValidity()) {
|
||||
const email = formEl.querySelector('input[name="email"]').value;
|
||||
const formData = new FormData();
|
||||
formData.append('email', email);
|
||||
|
||||
fetch(this.actionPath, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
credentials: 'same-origin', // Needed for admin permissions to be sent.
|
||||
}).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
alert('Thanks. But that user already exists');
|
||||
throw new Error('User already added');
|
||||
} else if (resp.status !== 201) {
|
||||
throw new Error('Sever error adding new user');
|
||||
}
|
||||
return resp.json();
|
||||
}).then((json) => {
|
||||
this.addUser(json);
|
||||
formEl.reset();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ajaxDelete(e) {
|
||||
e.preventDefault();
|
||||
if (!confirm('Remove user?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idx = e.target.dataset.index;
|
||||
|
||||
fetch(e.currentTarget.href, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
}).then(() => {
|
||||
this.removeUser(idx);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link rel="stylesheet" href="/static/css/elements/chromedash-userlist.css">
|
||||
|
||||
<form id="form" name="user_form" method="post" action="${this.actionPath}" onsubmit="return false;">
|
||||
<input type="email" placeholder="Email address" name="email" id="id_email" required>
|
||||
<td><input type="submit" @click="${this.ajaxSubmit}">
|
||||
</form>
|
||||
<ul id="user-list">
|
||||
${this.users.map((user, index) => html`
|
||||
<li>
|
||||
<a href="${this.actionPath}/${user.id}" data-index="${index}" @click="${this.ajaxDelete}">delete</a>
|
||||
<span>${user.email}</span>
|
||||
</li>
|
||||
`)}
|
||||
</ul>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('chromedash-userlist', ChromedashUserlist);
|
|
@ -12,7 +12,7 @@ for (let i = 0; i < fields.length; ++i) {
|
|||
const MIN_MILESTONE_TO_BE_ACTIVE = 3;
|
||||
const NO_LONGER_PURSUING = 1000;
|
||||
|
||||
document.querySelector('[name="feature_form"]').addEventListener('change', (e) => {
|
||||
document.querySelector('[name=feature_form]').addEventListener('change', (e) => {
|
||||
switch (e.target.tagName.toLowerCase()) {
|
||||
case 'select':
|
||||
if (e.target.id === 'id_impl_status_chrome') {
|
||||
|
@ -27,20 +27,23 @@ document.querySelector('[name="feature_form"]').addEventListener('change', (e) =
|
|||
}
|
||||
});
|
||||
|
||||
document.querySelector('.delete-button').addEventListener('click', (e) => {
|
||||
if (!confirm('Delete feature?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`/admin/features/delete/${e.currentTarget.dataset.id}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
}).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
location.href = '/features';
|
||||
// Only admins see this button
|
||||
if (document.querySelector('.delete-button')) {
|
||||
document.querySelector('.delete-button').addEventListener('click', (e) => {
|
||||
if (!confirm('Delete feature?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`/admin/features/delete/${e.currentTarget.dataset.id}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
}).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
location.href = '/features';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the chrome milestone inputs.
|
||||
|
|
|
@ -8,12 +8,10 @@ document.querySelector('paper-toggle-button').addEventListener('change', e => {
|
|||
document.querySelector('chromedash-schedule').hideBlink = e.target.checked;
|
||||
});
|
||||
|
||||
document.addEventListener('WebComponentsReady', function() {
|
||||
const header = document.querySelector('app-header-layout app-header');
|
||||
if (header) {
|
||||
header.fixed = false;
|
||||
}
|
||||
});
|
||||
const header = document.querySelector('app-header-layout app-header');
|
||||
if (header) {
|
||||
header.fixed = false;
|
||||
}
|
||||
|
||||
async function init() {
|
||||
document.body.classList.remove('loading');
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
:host {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding-bottom: $content-padding * 5;
|
||||
padding: 0 1em $content-padding * 5;
|
||||
margin-right: $content-padding * -1;
|
||||
}
|
||||
|
||||
iron-icon {
|
||||
|
@ -76,15 +77,11 @@ iron-icon {
|
|||
padding: $content-padding;
|
||||
margin-bottom: $content-padding;
|
||||
border-radius: $default-border-radius;
|
||||
flex: 1 0 calc(33.33% - 16px);
|
||||
max-width: 33.33%;
|
||||
flex: 1 0 0;
|
||||
min-width: 300px;
|
||||
margin-right: $content-padding;
|
||||
counter-reset: featurecount;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: $content-padding;
|
||||
}
|
||||
|
||||
&.no-components {
|
||||
.feature_components {
|
||||
display: none;
|
||||
|
@ -194,11 +191,3 @@ iron-icon {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 700px) {
|
||||
:host {
|
||||
.release {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
@import "element";
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-top: 10px;
|
||||
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
@import "../vars";
|
||||
|
||||
/* Fix a bug. Left align the right side list and the header.
|
||||
* TODO: Find out why it happens, and fix the root cause. */
|
||||
app-drawer-layout:not([narrow]) app-header-layout {
|
||||
margin-left: -60px;
|
||||
}
|
||||
|
||||
#content {
|
||||
// &.ready {
|
||||
// #spinner {
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
@import "vars";
|
||||
|
||||
/* Fix a bug. Left align the right side list and the header.
|
||||
* TODO: Find out why it happens, and fix the root cause. */
|
||||
app-drawer-layout:not([narrow]) app-header-layout {
|
||||
margin-left: -60px;
|
||||
}
|
||||
|
||||
paper-item {
|
||||
display: block;
|
||||
padding: 12px 16px;
|
||||
|
|
|
@ -42,7 +42,7 @@ limitations under the License.
|
|||
|
||||
<style>
|
||||
{% inline_file "/static/css/main.css" %}
|
||||
app-drawer {
|
||||
:root {
|
||||
--app-drawer-width: 200px; /* Used by the component. */
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
{% load inline_file %}
|
||||
|
||||
{% block preload %}
|
||||
<script type="module" src="/static/elements/admin-imports.js"></script>
|
||||
<script type="module">
|
||||
import 'https://unpkg.com/@polymer/paper-toggle-button/paper-toggle-button.js?module';
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<style>{% inline_file "/static/css/blink.css" %}</style>
|
||||
<style>{% inline_file "/static/css/blink.css" %}</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block drawer %}
|
||||
|
@ -100,12 +102,12 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
$('paper-toggle-button').addEventListener('change', e => {
|
||||
document.querySelector('paper-toggle-button').addEventListener('change', e => {
|
||||
e.stopPropagation();
|
||||
$('#components_list').classList.toggle('editing', e.target.checked);
|
||||
document.querySelector('#components_list').classList.toggle('editing', e.target.checked);
|
||||
});
|
||||
|
||||
$('#components_list').addEventListener('click', function(e) {
|
||||
document.querySelector('#components_list').addEventListener('click', function(e) {
|
||||
const addUser = e.target.classList.contains('add_owner_button');
|
||||
const removeUser = e.target.classList.contains('remove_owner_button');
|
||||
|
||||
|
@ -178,7 +180,7 @@ window.addEventListener('DOMContentLoaded', function(e) {
|
|||
if (location.hash) {
|
||||
setTimeout(function() {
|
||||
const el = document.getElementById(location.hash.slice(1));
|
||||
$('app-header').scroll(0, el.offsetTop);
|
||||
document.querySelector('app-header').scroll(0, el.offsetTop);
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
{% extends "_base.html" %}
|
||||
|
||||
{% block preload %}
|
||||
<script type="module" src="/static/elements/admin-imports.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<link rel="stylesheet" href="/static/css/forms.css">
|
||||
<style>
|
||||
|
@ -46,5 +42,5 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="/static/js/admin/feature_form.js"></script>
|
||||
<script src="/static/js/admin/feature_form.min.js"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
{% extends "_base.html" %}
|
||||
{% load inline_file %}
|
||||
|
||||
{% block preload %}
|
||||
<script type="module" src="/static/elements/feature-imports.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<!-- <link rel="stylesheet" href="/static/css/forms.css"> -->
|
||||
<!-- <link rel="stylesheet" href="/static/css/features/launch.css"> -->
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
{% extends "_base.html" %}
|
||||
|
||||
{% block preload %}
|
||||
<script type="module" src="/static/elements/admin-imports.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<link rel="stylesheet" href="/static/css/forms.css">
|
||||
{% endblock %}
|
||||
|
@ -40,5 +36,5 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="/static/js/admin/feature_form.js"></script>
|
||||
<script src="/static/js/admin/feature_form.min.js"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
{% block js %}
|
||||
<script>
|
||||
{% inline_file "/static/js/notifications.js" %}
|
||||
{% inline_file "/static/js/notifications.min.js" %}
|
||||
|
||||
const SERVER_KEY = '{{FIREBASE_SERVER_KEY}}';
|
||||
const SUBSCRIPTIONS = {{subscriptions|safe}};
|
||||
|
@ -90,7 +90,7 @@
|
|||
}
|
||||
|
||||
async function init() {
|
||||
$('#num_push_tokens').textContent = SUBSCRIPTIONS.length;
|
||||
document.querySelector('#num_push_tokens').textContent = SUBSCRIPTIONS.length;
|
||||
document.querySelectorAll('.push-notifications').forEach(el => {
|
||||
el.removeAttribute('hidden');
|
||||
el.disabled = false;
|
||||
|
|
|
@ -2,15 +2,11 @@
|
|||
{% load verbatim %}
|
||||
{% load inline_file %}
|
||||
|
||||
{% block preload %}
|
||||
<script type="module" src="/static/elements/admin-imports.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<style>
|
||||
{% inline_file "/static/css/schedule.css" %}
|
||||
{% inline_file "/static/css/subscribers.css" %}
|
||||
</style>
|
||||
<style>
|
||||
{% inline_file "/static/css/schedule.css" %}
|
||||
{% inline_file "/static/css/subscribers.css" %}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block drawer %}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
{% extends "_base.html" %}
|
||||
|
||||
{% block css %}
|
||||
<!-- <link rel="stylesheet" href="/static/css/users/users.css"> -->
|
||||
<link rel="stylesheet" href="/static/css/forms.css">
|
||||
<link rel="stylesheet" href="/static/css/forms.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block preload %}
|
||||
<script type="module" src="/static/elements/admin-imports.js"></script>
|
||||
<script type="module">
|
||||
import '/static/elements/chromedash-userlist.js';
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block subheader %}
|
||||
|
@ -25,11 +26,9 @@
|
|||
|
||||
{% block js %}
|
||||
<script>
|
||||
document.addEventListener('WebComponentsReady', () => {
|
||||
document.body.classList.remove('loading');
|
||||
|
||||
const el = document.querySelector('chromedash-userlist');
|
||||
el.users = {{users|safe}}; // Filled from server.
|
||||
});
|
||||
el.users = {{users|safe}};
|
||||
|
||||
document.body.classList.remove('loading');
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
{% block preload %}
|
||||
<script type="module">
|
||||
|
||||
import 'https://unpkg.com/@polymer/iron-icon/iron-icon.js?module';
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -248,7 +248,7 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Variables used in feature.js
|
||||
// Variables used in feature-page.js
|
||||
const FEATURE_ID = '{{ feature.id }}';
|
||||
const FEATURE_NAME = '{{ feature.name|escapejs }}';
|
||||
const FEATUER_SUMMARY = '{{ feature.summary|escapejs }}';
|
||||
|
|
Загрузка…
Ссылка в новой задаче