Skip to content

Commit

Permalink
FEATURE: Add inline grouping pattern creation (#219)
Browse files Browse the repository at this point in the history
  • Loading branch information
janzenisaac committed Jan 22, 2024
1 parent 43c6d5c commit b726b74
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 13 deletions.
82 changes: 81 additions & 1 deletion client-app/app/controllers/index.js
Expand Up @@ -13,6 +13,8 @@ import { tracked } from "@glimmer/tracking";
@classic
export default class IndexController extends Controller {
@tracked loading = false;
@tracked buildingGroupingPattern = false;
@tracked rowMessagesForGroupingPattern = [];

showDebug = getLocalStorage("showDebug", false);
showInfo = getLocalStorage("showInfo", false);
Expand All @@ -33,6 +35,13 @@ export default class IndexController extends Controller {
);
}

get showCreateGroupingPattern() {
return (
this.buildingGroupingPattern &&
this.rowMessagesForGroupingPattern.length > 1
);
}

@computed("search")
get searchTerm() {
if (this.search) {
Expand Down Expand Up @@ -67,6 +76,19 @@ export default class IndexController extends Controller {
this.model.selectRow(row, opts);
}

@action
handleCheckboxChange(row, event) {
if (event.target.checked) {
this.rowMessagesForGroupingPattern = [
...this.rowMessagesForGroupingPattern,
row.message,
];
} else {
this.rowMessagesForGroupingPattern =
this.rowMessagesForGroupingPattern.filter((i) => i !== row.message);
}
}

@action
tabChangedAction(newTab) {
this.model.tabChanged(newTab);
Expand All @@ -87,7 +109,6 @@ export default class IndexController extends Controller {
// eslint-disable-next-line no-alert
if (confirm("Clear the logs?\n\nCancel = No, OK = Clear")) {
await ajax("/clear", { type: "POST" });
this.loading = true;
this.model.reload();
this.loading = false;
}
Expand Down Expand Up @@ -171,4 +192,63 @@ export default class IndexController extends Controller {

debounce(this, this.doSearch, term, 250);
}

@action
toggleGroupingPatternFromSelectedRows() {
this.buildingGroupingPattern = !this.buildingGroupingPattern;
this.rowMessagesForGroupingPattern = [];
}

@action
async createGroupingPatternFromSelectedRows() {
let match = this.findLongestMatchingPrefix(
this.rowMessagesForGroupingPattern
);
match = this.escapeRegExp(match);

if (
match.trim().length &&
// eslint-disable-next-line no-alert
confirm(
`Do you want to create the grouping pattern\n\n"${match}"\n\nCancel = No, OK = Create`
)
) {
await ajax("/patterns/grouping.json", {
method: "POST",
data: {
pattern: match,
},
});
this.rowMessagesForGroupingPattern = [];
this.buildingGroupingPattern = false;
this.model.reload();
} else if (!match.trim().length) {
// eslint-disable-next-line no-alert
alert("Can not create a grouping pattern with the given rows");
}
}

findLongestMatchingPrefix(strings) {
const shortestString = strings.reduce(
(shortest, str) => (str.length < shortest.length ? str : shortest),
strings[0]
);

let longestMatchingSubstring = "";
for (let i = 0; i < shortestString.length; i++) {
const currentSubstring = shortestString.substring(0, i + 1);

if (strings.every((str) => str.includes(currentSubstring))) {
longestMatchingSubstring = currentSubstring;
} else {
break;
}
}

return longestMatchingSubstring;
}

escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}
}
23 changes: 16 additions & 7 deletions client-app/app/styles/app.css
Expand Up @@ -52,9 +52,19 @@ tbody tr {
margin: 40dvh auto;
}

.message-row-wrapper {
display: flex;
width: inherit;

.grouping-checkbox {
margin: 0;
}
}

.message-row {
font-family: "Roboto";
display: flex;
width: inherit;
}

.message-row .severity,
Expand Down Expand Up @@ -306,7 +316,7 @@ label span {
}

.severity-filters label {
margin-right: 18px;
margin-right: 10px;
}

.search-clear-all .clear {
Expand Down Expand Up @@ -396,7 +406,7 @@ label span {
.btn {
display: inline-block;
margin: 0;
padding: 5px 12px;
padding: 5px 8px;
font-size: 1em;
line-height: 0;
text-align: center;
Expand Down Expand Up @@ -455,7 +465,6 @@ label span {
.search-clear-all .footer-btns .settings {
height: 100%;
box-sizing: border-box;
margin: 0 7px 0 7px;
display: inline-flex;
align-items: center;
}
Expand All @@ -471,11 +480,11 @@ label span {
}

.footer-btns {
flex-grow: 0;
flex-shrink: 0;
display: flex;
gap: 0.7em;
}

@media (min-width: 771px) {
@media (min-width: 871px) {
.severity-filters,
.search-clear-all {
height: 100%;
Expand All @@ -497,7 +506,7 @@ label span {
}
}

@media (max-width: 770px) {
@media (max-width: 870px) {
.severity-filters {
padding: 10px;
}
Expand Down
29 changes: 28 additions & 1 deletion client-app/app/templates/index.hbs
Expand Up @@ -17,7 +17,16 @@
{{/if}}

{{#each this.model.rows as |row|}}
<MessageRow @model={{row}} @selectRow={{fn this.selectRowAction row}} />
<div class="message-row-wrapper">
{{#if this.buildingGroupingPattern}}
<input
type="checkbox"
class="grouping-checkbox"
{{on "change" (fn this.handleCheckboxChange row)}}
/>
{{/if}}
<MessageRow @model={{row}} @selectRow={{fn this.selectRowAction row}} />
</div>
{{/each}}
</div>
</div>
Expand Down Expand Up @@ -109,9 +118,27 @@

<div class="footer-btns">
{{#if this.showSettings}}
{{#if this.showCreateGroupingPattern}}
<button
class="settings btn"
type="button"
{{on "click" this.createGroupingPatternFromSelectedRows}}
>
<span>Create Grouping Pattern</span>
</button>
{{/if}}

<LinkTo @route="settings" class="settings btn no-text">
<FaIcon @icon="cog" />
</LinkTo>

<button
class="settings btn no-text"
type="button"
{{on "click" this.toggleGroupingPatternFromSelectedRows}}
>
<FaIcon @icon="list" />
</button>
{{/if}}

<button
Expand Down
1 change: 1 addition & 0 deletions client-app/config/icons.js
Expand Up @@ -19,6 +19,7 @@ module.exports = function () {
"cog",
"lock",
"unlock",
"list",
],
"free-regular-svg-icons": ["trash-alt", "check-square", "copy", "clone"],
// "free-brands-svg-icons": []
Expand Down
33 changes: 32 additions & 1 deletion client-app/tests/unit/controllers/index-test.js
Expand Up @@ -6,10 +6,10 @@ import * as utilities from "client-app/lib/utilities";

module("Unit | Controller | index", function (hooks) {
setupTest(hooks);
const ajaxStub = sinon.stub(utilities, "ajax");

test("uses search param to filter results", function (assert) {
const controller = this.owner.lookup("controller:index");
const ajaxStub = sinon.stub(utilities, "ajax");
const messages = MessageCollection.create();
const row1 = { message: "error tomtom", severity: 2, key: "ce1f53b0cc" };
const row2 = { message: "error steaky", severity: 3, key: "b083352825" };
Expand Down Expand Up @@ -43,4 +43,35 @@ module("Unit | Controller | index", function (hooks) {
"with correct terms"
);
});

test("Creating inline grouping patterns finds the longest matching prefix between selected messages", function (assert) {
const controller = this.owner.lookup("controller:index");
const messages = ["error foo tomtom", "error foo steaky", "error foo bar"];

assert.deepEqual(
controller.findLongestMatchingPrefix(messages),
"error foo "
);
});

test("Creating inline grouping patterns can handle special characters", function (assert) {
const controller = this.owner.lookup("controller:index");
let messages = [
"error foo!/@ tomtom",
"error foo!/@ steaky",
"error foo!/@ bar",
];

assert.deepEqual(
controller.findLongestMatchingPrefix(messages),
"error foo!/@ "
);

messages = ["/$home/sam/.r\benv/", "/$home/sam/.r\benv/"];

assert.deepEqual(
controller.findLongestMatchingPrefix(messages),
"/$home/sam/.r\benv/"
);
});
});
2 changes: 1 addition & 1 deletion lib/logster/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Logster
VERSION = "2.15.0"
VERSION = "2.16.0"
end
4 changes: 2 additions & 2 deletions website/Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: ..
specs:
logster (2.14.0)
logster (2.15.0)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -33,4 +33,4 @@ DEPENDENCIES
sinatra

BUNDLED WITH
2.2.16
2.5.3

0 comments on commit b726b74

Please sign in to comment.