Gisle Aune
6 years ago
15 changed files with 409 additions and 23 deletions
-
8marko/components/menu-link/component.js
-
2marko/page/logs-content/components/logs-content-menu/index.marko
-
147marko/page/logs/components/add-filter-modal-body/component.js
-
12marko/page/logs/components/add-filter-modal-body/index.marko
-
16marko/page/logs/components/add-filter-modal/component.js
-
7marko/page/logs/components/add-filter-modal/index.marko
-
16marko/page/logs/components/add-filter-modal/style.less
-
64marko/page/logs/components/logs-menu/component.js
-
6marko/page/logs/components/logs-menu/index.marko
-
37marko/page/logs/components/page/component.js
-
5marko/page/logs/components/page/index.marko
-
2marko/page/logs/list.marko
-
22routes/logs/index.js
-
49rpdata/api/Character.js
-
39rpdata/api/LogHeader.js
@ -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 { |
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) { |
select(value) { |
||||
this.emit("select", value) |
this.emit("select", value) |
||||
} |
} |
@ -1,10 +1,12 @@ |
|||||
<menu user=input.user> |
<menu user=input.user> |
||||
<menu-header key="logs">Logs</menu-header> |
<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"> |
<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> |
</if-permitted> |
||||
<menu-gap /> |
<menu-gap /> |
||||
<menu-header key="filters">Filters</menu-header> |
<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-gap /> |
||||
</menu> |
</menu> |
@ -1,6 +1,7 @@ |
|||||
<background src="/assets/images/bg.png" opacity=0.25 /> |
<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> |
<main> |
||||
<logs-list logs=state.logs /> |
<logs-list logs=state.logs /> |
||||
</main> |
</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"})> |
<include("../layout", {title: "Logs", site: "logs"})> |
||||
<@body> |
<@body> |
||||
<!-- Page needed to get component.js functionality --> |
<!-- 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> |
</@body> |
||||
</include> |
</include> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue