Add initial frontend
This commit is contained in:
parent
68c2f8f676
commit
bd8cdc8509
8 changed files with 334 additions and 0 deletions
29
client/css/banned-posts.styl
Normal file
29
client/css/banned-posts.styl
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
.content-wrapper.banned-posts
|
||||||
|
width: 100%
|
||||||
|
max-width: 45em
|
||||||
|
table
|
||||||
|
border-spacing: 0
|
||||||
|
width: 100%
|
||||||
|
tr.default td
|
||||||
|
background: $default-banned-post-background-color
|
||||||
|
td, th
|
||||||
|
padding: .4em
|
||||||
|
&.color
|
||||||
|
input[type=text]
|
||||||
|
width: 8em
|
||||||
|
&.usages
|
||||||
|
text-align: center
|
||||||
|
&.remove, &.set-default
|
||||||
|
white-space: pre
|
||||||
|
th
|
||||||
|
white-space: nowrap
|
||||||
|
&:first-child
|
||||||
|
padding-left: 0
|
||||||
|
&:last-child
|
||||||
|
padding-right: 0
|
||||||
|
tfoot
|
||||||
|
display: none
|
||||||
|
form
|
||||||
|
width: auto
|
13
client/html/banned_post_entry.tpl
Normal file
13
client/html/banned_post_entry.tpl
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<tr data-category='<%- ctx.postBan.checksum %>'><%
|
||||||
|
<td class='name'>
|
||||||
|
<%- ctx.postBan.checksum %>
|
||||||
|
</td>
|
||||||
|
<td class='time'>
|
||||||
|
<%- ctx.makeRelativeTime(ctx.postBan.time) %>
|
||||||
|
</td>
|
||||||
|
<% if (ctx.canDelete) { %>
|
||||||
|
<td class='remove'>
|
||||||
|
<a href>Unban</a>
|
||||||
|
</td>
|
||||||
|
<% } %>
|
||||||
|
</tr>
|
25
client/html/banned_post_list.tpl
Normal file
25
client/html/banned_post_list.tpl
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<div class='content-wrapper banned-posts'>
|
||||||
|
<form>
|
||||||
|
<h1>Banned posts</h1>
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class='checksum'>Checksum</th>
|
||||||
|
<th class='time'>Time of ban</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='messages'></div>
|
||||||
|
|
||||||
|
<% if (ctx.canDelete) { %>
|
||||||
|
<div class='buttons'>
|
||||||
|
<input type='submit' class='save' value='Save changes'>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</form>
|
||||||
|
</div>
|
59
client/js/controllers/banned_post_controller.js
Normal file
59
client/js/controllers/banned_post_controller.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const api = require("../api.js");
|
||||||
|
const BannedPostList = require("../models/banned_post_list.js");
|
||||||
|
const topNavigation = require("../models/top_navigation.js");
|
||||||
|
const BannedPostsView = require("../views/banned_posts_view.js");
|
||||||
|
const EmptyView = require("../views/empty_view.js");
|
||||||
|
|
||||||
|
class BannedPostController {
|
||||||
|
constructor() {
|
||||||
|
if (!api.hasPrivilege("posts:ban:list")) {
|
||||||
|
this._view = new EmptyView();
|
||||||
|
this._view.showError(
|
||||||
|
"You don't have privileges to view banned posts."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
topNavigation.activate("banned-posts");
|
||||||
|
topNavigation.setTitle("Listing banned posts");
|
||||||
|
BannedPostList.get().then(
|
||||||
|
(response) => {
|
||||||
|
this._bannedPosts = response.results;
|
||||||
|
this._view = new BannedPostsView({
|
||||||
|
bannedPosts: this._bannedPosts,
|
||||||
|
canDelete: api.hasPrivilege("poolCategories:delete")
|
||||||
|
});
|
||||||
|
this._view.addEventListener("submit", (e) =>
|
||||||
|
this._evtSubmit(e)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this._view = new EmptyView();
|
||||||
|
this._view.showError(error.message);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtSubmit(e) {
|
||||||
|
this._view.clearMessages();
|
||||||
|
this._view.disableForm();
|
||||||
|
this._bannedPosts.save().then(
|
||||||
|
() => {
|
||||||
|
this._view.enableForm();
|
||||||
|
this._view.showSuccess("Changes saved.");
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this._view.enableForm();
|
||||||
|
this._view.showError(error.message);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = (router) => {
|
||||||
|
router.enter(["banned-posts"], (ctx, next) => {
|
||||||
|
ctx.controller = new BannedPostController(ctx, next);
|
||||||
|
});
|
||||||
|
};
|
|
@ -83,6 +83,7 @@ Promise.resolve()
|
||||||
controllers.push(
|
controllers.push(
|
||||||
require("./controllers/user_registration_controller.js")
|
require("./controllers/user_registration_controller.js")
|
||||||
);
|
);
|
||||||
|
controllers.push(require("./controllers/banned_post_controller.js"));
|
||||||
|
|
||||||
// 404 controller needs to be registered last
|
// 404 controller needs to be registered last
|
||||||
controllers.push(require("./controllers/not_found_controller.js"));
|
controllers.push(require("./controllers/not_found_controller.js"));
|
||||||
|
|
57
client/js/models/banned_post.js
Normal file
57
client/js/models/banned_post.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const api = require("../api.js");
|
||||||
|
const uri = require("../util/uri.js");
|
||||||
|
const events = require("../events.js");
|
||||||
|
|
||||||
|
class BannedPost extends events.EventTarget {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._checksum = "";
|
||||||
|
this._time = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
get checksum() {
|
||||||
|
return this._checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
get time() {
|
||||||
|
return this._time;
|
||||||
|
}
|
||||||
|
|
||||||
|
set checksum(value) {
|
||||||
|
this._checksum = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set time(value) {
|
||||||
|
this._time = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromResponse(response) {
|
||||||
|
const ret = new BannedPost();
|
||||||
|
ret._updateFromResponse(response);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete() {
|
||||||
|
return api
|
||||||
|
.delete(uri.formatApiLink("post-ban", this._checksum))
|
||||||
|
.then((response) => {
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent("delete", {
|
||||||
|
detail: {
|
||||||
|
bannedPost: this,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return Promise.resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateFromResponse(response) {
|
||||||
|
this._checksum = response.checksum;
|
||||||
|
this.time = response.time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BannedPost;
|
47
client/js/models/banned_post_list.js
Normal file
47
client/js/models/banned_post_list.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
const api = require("../api.js");
|
||||||
|
const uri = require("../util/uri.js");
|
||||||
|
const AbstractList = require("./abstract_list.js");
|
||||||
|
const BannedPost = require("./banned_post.js");
|
||||||
|
|
||||||
|
class BannedPostList extends AbstractList {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._deletedBans = [];
|
||||||
|
this.addEventListener("remove", (e) => this._evtBannedPostDeleted(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
static get() {
|
||||||
|
return api
|
||||||
|
.get(uri.formatApiLink("post-ban"))
|
||||||
|
.then((response) => {
|
||||||
|
return Promise.resolve(
|
||||||
|
Object.assign({}, response, {
|
||||||
|
results: BannedPostList.fromResponse(
|
||||||
|
response.results
|
||||||
|
),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
let promises = [];
|
||||||
|
for (let BannedPost of this._deletedBans) {
|
||||||
|
promises.push(BannedPost.delete());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(promises).then((response) => {
|
||||||
|
this._deletedBans = [];
|
||||||
|
return Promise.resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtBannedPostDeleted(e) {
|
||||||
|
this._deletedBans.push(e.detail.BannedPost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BannedPostList._itemClass = BannedPost;
|
||||||
|
BannedPostList._itemName = "bannedPost";
|
||||||
|
|
||||||
|
module.exports = BannedPostList;
|
103
client/js/views/banned_posts_view.js
Normal file
103
client/js/views/banned_posts_view.js
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const events = require("../events.js");
|
||||||
|
const views = require("../util/views.js");
|
||||||
|
const BannedPost = require("../models/banned_post.js");
|
||||||
|
|
||||||
|
const template = views.getTemplate("banned-post-list");
|
||||||
|
const rowTemplate = views.getTemplate("banned-post-entry");
|
||||||
|
|
||||||
|
class BannedPostsView extends events.EventTarget {
|
||||||
|
constructor(ctx) {
|
||||||
|
super();
|
||||||
|
this._ctx = ctx;
|
||||||
|
this._hostNode = document.getElementById("content-holder");
|
||||||
|
|
||||||
|
views.replaceContent(this._hostNode, template(ctx));
|
||||||
|
views.syncScrollPosition();
|
||||||
|
views.decorateValidator(this._formNode);
|
||||||
|
|
||||||
|
const bannedPostsToAdd = Array.from(ctx.bannedPosts);
|
||||||
|
for (let bannedPost of bannedPostsToAdd) {
|
||||||
|
this._addBannedPostRowNode(bannedPost);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.bannedPosts.addEventListener("remove", (e) =>
|
||||||
|
this._evtBannedPostDeleted(e)
|
||||||
|
);
|
||||||
|
|
||||||
|
this._formNode.addEventListener("submit", (e) =>
|
||||||
|
this._evtSaveButtonClick(e, ctx)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
enableForm() {
|
||||||
|
views.enableForm(this._formNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
disableForm() {
|
||||||
|
views.disableForm(this._formNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMessages() {
|
||||||
|
views.clearMessages(this._hostNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
showSuccess(message) {
|
||||||
|
views.showSuccess(this._hostNode, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
showError(message) {
|
||||||
|
views.showError(this._hostNode, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
get _formNode() {
|
||||||
|
return this._hostNode.querySelector("form");
|
||||||
|
}
|
||||||
|
|
||||||
|
get _tableBodyNode() {
|
||||||
|
return this._hostNode.querySelector("tbody");
|
||||||
|
}
|
||||||
|
|
||||||
|
_addBannedPostRowNode(bannedPost) {
|
||||||
|
const rowNode = rowTemplate(
|
||||||
|
Object.assign({}, this._ctx, { postBan: bannedPost })
|
||||||
|
);
|
||||||
|
|
||||||
|
const removeLinkNode = rowNode.querySelector(".remove a");
|
||||||
|
if (removeLinkNode) {
|
||||||
|
removeLinkNode.addEventListener("click", (e) =>
|
||||||
|
this._evtDeleteButtonClick(e, rowNode)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tableBodyNode.appendChild(rowNode);
|
||||||
|
|
||||||
|
rowNode._bannedPost = bannedPost;
|
||||||
|
bannedPost._rowNode = rowNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeBannedPostRowNode(bannedPost) {
|
||||||
|
const rowNode = bannedPost._rowNode;
|
||||||
|
rowNode.parentNode.removeChild(rowNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtBannedPostDeleted(e) {
|
||||||
|
this._removeBannedPostRowNode(e.detail.poolCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtDeleteButtonClick(e, rowNode, link) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.target.classList.contains("inactive")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._ctx.bannedPosts.remove(rowNode._bannedPost);
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtSaveButtonClick(e, ctx) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.dispatchEvent(new CustomEvent("submit"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BannedPostsView;
|
Loading…
Reference in a new issue