logs: Added filtering.
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
onCreate() {
|
||||
this.state = {
|
||||
classes: ["menu-link", "color-menu"],
|
||||
classes: [],
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
this.state.classes = ["menu-link", "color-menu"]
|
||||
|
||||
if (input.dark) {
|
||||
this.state.classes.push(input)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<menu-gap />
|
||||
</if-permitted>
|
||||
<menu-header>Links</menu-header>
|
||||
<menu-link href=("/logs/?channels="+input.log.channel.name) icon="#">${input.log.channel.name}</menu-link>
|
||||
<menu-link href=("/logs/?channels="+encodeURIComponent(input.log.channel.name)) icon="#">${input.log.channel.name}</menu-link>
|
||||
<menu-link if(input.log.channel.eventName) href=("/logs/?channels="+input.log.channel.eventName) icon="E">${input.log.channel.eventName}</menu-link>
|
||||
<for(character in input.log.characters)>
|
||||
<menu-link href=("/logs/?characters="+character.id) icon="C">${character.name} <span class="weak">(${character.author})</span></menu-link>
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
const {channelApi} = require("../../../../../rpdata/api/Channel")
|
||||
const {logHeaderApi} = require("../../../../../rpdata/api/LogHeader")
|
||||
|
||||
module.exports = class {
|
||||
onCreate() {
|
||||
this.state = {
|
||||
search: "",
|
||||
filters: [],
|
||||
channels: [],
|
||||
eventNames: [],
|
||||
}
|
||||
|
||||
this.first = true
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
if (input.filters) {
|
||||
if (this.timeout != null) {
|
||||
setTimeout(() => {
|
||||
this.searchFilters()
|
||||
}, 1)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(input.eventNames)
|
||||
}
|
||||
|
||||
onMount() {
|
||||
this.timeout = null
|
||||
|
||||
if (this.state.channels.length === 0) {
|
||||
channelApi.list().then(channels => {
|
||||
this.state.channels = channels
|
||||
this.searchFilters()
|
||||
}).catch(err => {
|
||||
console.warn("Failed to fetch channels:", err)
|
||||
})
|
||||
}
|
||||
|
||||
if (this.state.eventNames.length === 0) {
|
||||
logHeaderApi.eventNames().then(eventNames => {
|
||||
this.state.eventNames = eventNames
|
||||
this.searchFilters()
|
||||
}).catch(err => {
|
||||
console.warn("Failed to fetch eventNames:", err)
|
||||
})
|
||||
}
|
||||
|
||||
this.getEl("search").addEventListener("keydown", ev => {
|
||||
if (this.waiting) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.timeout != null) {
|
||||
clearTimeout(this.timeout)
|
||||
}
|
||||
|
||||
this.timeout = setTimeout(() => {
|
||||
this.timeout = null
|
||||
this.state.search = ev.target.value
|
||||
|
||||
this.searchFilters()
|
||||
}, 200)
|
||||
})
|
||||
}
|
||||
|
||||
onUnmount() {
|
||||
clearTimeout(this.timeout)
|
||||
this.timeout = null
|
||||
|
||||
this.state.search = ""
|
||||
this.state.filters = []
|
||||
}
|
||||
|
||||
add(type, value, filter) {
|
||||
this.emit("add", type, value)
|
||||
|
||||
this.state.filters = this.state.filters.filter(f => f != filter)
|
||||
}
|
||||
|
||||
searchFilters() {
|
||||
const filters = []
|
||||
const search = this.state.search.toLowerCase()
|
||||
|
||||
for (const channel of this.state.channels) {
|
||||
if (search == "" || channel.name.toLowerCase().includes(search)) {
|
||||
if (this.input.filter.channels && this.input.filter.channels.includes(channel.name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
filters.push({
|
||||
colorClass: "color-tag-location",
|
||||
text: channel.name,
|
||||
type: "channels",
|
||||
value: channel.name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for (const name of this.state.eventNames) {
|
||||
if (search == "" || name.toLowerCase().includes(search)) {
|
||||
if (this.input.filter.events && this.input.filter.events.includes(name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
filters.push({
|
||||
colorClass: "color-tag-event",
|
||||
text: name,
|
||||
type: "events",
|
||||
value: name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for (const character of this.input.characters) {
|
||||
if (search == "" || character.name.toLowerCase().includes(search)) {
|
||||
if (this.input.filter.characters && this.input.filter.characters.includes(character.id)) {
|
||||
continue
|
||||
}
|
||||
|
||||
filters.push({
|
||||
colorClass: "color-tag-character",
|
||||
text: character.name,
|
||||
type: "characters",
|
||||
value: character.id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
filters.sort((a, b) => {
|
||||
let aCompare = a.text
|
||||
let bCompare = b.text
|
||||
|
||||
if (a.text.charAt(0) === "#") {
|
||||
aCompare = a.text.slice(1)
|
||||
}
|
||||
if (b.text.charAt(0) === "#") {
|
||||
bCompare = b.text.slice(1)
|
||||
}
|
||||
|
||||
return aCompare.localeCompare(bCompare)
|
||||
})
|
||||
|
||||
this.state.filters = filters
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<label>Search</label>
|
||||
<input key="search" placeholder="" class="big" value=state.search />
|
||||
|
||||
<label>Filters</label>
|
||||
<div if(state.search.length > 0) class="filter-row" >
|
||||
<div class=["content", "color-text"]>Search: ${state.search}</div>
|
||||
<button disabled=state.loading on-click("emit", "add", "search", state.search)>${input.filter.search ? "Replace" : "Add"}</button>
|
||||
</div>
|
||||
<div for(filter in state.filters) class="filter-row" >
|
||||
<div class=["content", filter.colorClass]>${filter.text}</div>
|
||||
<button disabled=state.loading on-click("add", filter.type, filter.value, filter)>Add</button>
|
||||
</div>
|
||||
@@ -0,0 +1,16 @@
|
||||
const moment = require("moment")
|
||||
|
||||
const {logsApi} = require("../../../../../rpdata/api/Log")
|
||||
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
filters: [],
|
||||
search: "",
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this.emit("close")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import moment from "moment"
|
||||
|
||||
<modal class="modal color-text nolabel" key="modal" enabled=(input.enabled) closable on-close("close") >
|
||||
<h1 key="stuff">Add Filter</h1>
|
||||
|
||||
<add-filter-modal-body characters=input.characters filter=input.filter on-add("emit", "add") />
|
||||
</modal>
|
||||
@@ -0,0 +1,16 @@
|
||||
div.filter-row {
|
||||
padding: 0.25em;
|
||||
border-bottom: 1px solid rgba(0, 220, 255, 0.125);
|
||||
|
||||
div.content {
|
||||
width: 80%;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 20% !important;
|
||||
display: inline-block !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,68 @@
|
||||
module.exports = class {
|
||||
onCreate() {
|
||||
this.state = {
|
||||
filters: [],
|
||||
filtered: false,
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
if (!input.filter) {
|
||||
this.state.filters = []
|
||||
this.state.filtered = false
|
||||
return
|
||||
}
|
||||
|
||||
const filters = []
|
||||
const {characters, channels, events, search} = input.filter
|
||||
|
||||
if (search != null) {
|
||||
filters.push({
|
||||
type: "search",
|
||||
key: "search",
|
||||
icon: "T",
|
||||
color: "color-text",
|
||||
text: search,
|
||||
})
|
||||
}
|
||||
|
||||
for (const character of (characters || [])) {
|
||||
filters.push({
|
||||
type: "characters",
|
||||
key: character,
|
||||
icon: "C",
|
||||
color: "color-tag-character",
|
||||
id: character,
|
||||
text: (input.characters.find(c => c.id === character) || {name: "Unknown ("+character+")"}).name,
|
||||
})
|
||||
}
|
||||
|
||||
for (const channel of (channels || [])) {
|
||||
filters.push({
|
||||
type: "channels",
|
||||
key: channel.replace(/'/g, "__"),
|
||||
icon: "#",
|
||||
color: "color-tag-location",
|
||||
id: channel,
|
||||
text: channel,
|
||||
})
|
||||
}
|
||||
|
||||
for (const event of (events || [])) {
|
||||
filters.push({
|
||||
type: "events",
|
||||
key: event.replace(/['\s\.]/g, "__"),
|
||||
icon: "E",
|
||||
color: "color-tag-event",
|
||||
id: event,
|
||||
text: event,
|
||||
})
|
||||
}
|
||||
|
||||
this.state.filters = filters
|
||||
this.state.filtered = (filters.length > 0)
|
||||
}
|
||||
|
||||
select(value) {
|
||||
this.emit("select", value)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<menu user=input.user>
|
||||
<menu-header key="logs">Logs</menu-header>
|
||||
<menu-link key="logs" selected=input.selected.index icon="L" href="/logs/">All</menu-link>
|
||||
<menu-link key="logs" selected=(input.selected.index && !state.filtered) icon="L" href="/logs/">All</menu-link>
|
||||
<if-permitted key="if-permitted" user=input.user permission="log.add">
|
||||
<menu-link key="add_log" dark on-click("select", "add") icon="+">Add Log</menu-link>
|
||||
<menu-link key="add_log" dark on-click("select", "log.add") icon="+">Add Log</menu-link>
|
||||
</if-permitted>
|
||||
<menu-gap />
|
||||
<menu-header key="filters">Filters</menu-header>
|
||||
<menu-link for(filter in state.filters) textClass=filter.color unselectable icon=filter.icon on-click("emit", "removefilter", filter.type, filter.id)>${filter.text}</menu-link>
|
||||
<menu-link key="add_filter" dark on-click("select", "filter.add") icon="+">Add Filter</menu-link>
|
||||
<menu-gap />
|
||||
</menu>
|
||||
@@ -3,6 +3,7 @@ const {logHeaderApi} = require("../../../../../rpdata/api/LogHeader")
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
filter: input.filter,
|
||||
logs: input.logs,
|
||||
modal: null,
|
||||
}
|
||||
@@ -16,8 +17,40 @@ module.exports = class {
|
||||
this.state.modal = null
|
||||
}
|
||||
|
||||
onMount() {
|
||||
logHeaderApi.list({limit: 0}).then(logs => {
|
||||
addFilter(type, filter) {
|
||||
if (type === "search") {
|
||||
this.state.filter = Object.assign({}, this.state.filter, {search: filter})
|
||||
} else {
|
||||
this.state.filter = Object.assign({}, this.state.filter, {[type]: (this.state.filter[type] || []).concat(filter)})
|
||||
}
|
||||
|
||||
console.log("FILTER:", this.state.filter)
|
||||
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
removeFilter(type, filter) {
|
||||
console.log(type, filter, this.state.filter)
|
||||
|
||||
if (type === "search") {
|
||||
this.state.filter = Object.assign({}, this.state.filter, {search: null})
|
||||
this.refresh()
|
||||
} else if ((this.state.filter[type] || []).length === 1) {
|
||||
this.state.filter = Object.assign({}, this.state.filter, {[type]: null})
|
||||
this.refresh()
|
||||
} else {
|
||||
const index = (this.state.filter[type] || []).indexOf(filter)
|
||||
if (index !== -1) {
|
||||
this.state.filter = Object.assign({}, this.state.filter, {[type]: this.state.filter[type].splice(0, index).concat(this.state.filter[type].splice(index + 1))},)
|
||||
this.refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refresh() {
|
||||
console.log("REFRESH", this.state.filter)
|
||||
|
||||
logHeaderApi.list(this.state.filter).then(logs => {
|
||||
this.state.logs = logs
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<background src="/assets/images/bg.png" opacity=0.25 />
|
||||
<logs-menu key="menu" on-select("open") selected=(input.selected || {}) user=input.user />
|
||||
<logs-menu key="menu" on-select("open") on-removefilter("removeFilter") selected=(input.selected || {}) user=input.user filter=state.filter characters=input.characters />
|
||||
<main>
|
||||
<logs-list logs=state.logs />
|
||||
</main>
|
||||
<add-log-modal enabled=(state.modal === "add") on-close("close") />
|
||||
<add-log-modal enabled=(state.modal === "log.add") on-close("close") />
|
||||
<add-filter-modal enabled=(state.modal === "filter.add") characters=input.characters filter=state.filter on-add("addFilter") on-close("close") />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<include("../layout", {title: "Logs", site: "logs"})>
|
||||
<@body>
|
||||
<!-- Page needed to get component.js functionality -->
|
||||
<page logs=input.logs user=input.user selected=input.selected />
|
||||
<page logs=input.logs filter=input.filter characters=input.characters user=input.user selected=input.selected />
|
||||
</@body>
|
||||
</include>
|
||||
Reference in New Issue
Block a user