Skip to content

Commit

Permalink
Add fuse-backed org search to superadmin org list (#2277)
Browse files Browse the repository at this point in the history
Closes #2276 

Adds a simple search bar to the superadmin interface that allows users
to search for orgs by org name, id, users (names and emails), and
subscriptions (subscription id and plan id).

[Extended search](https://www.fusejs.io/examples.html#extended-search)
is enabled, so exact search terms like `=stripe:sub_xxxxxxx` can be used
to find a specific org directly. [See the
docs](https://www.fusejs.io/examples.html#extended-search) for what
operators are available.

<img width="897" alt="Screenshot 2025-01-07 at 1 59 27 PM"
src="https://github.com/user-attachments/assets/56c22fd0-5a61-4665-b904-d4534079158a"
/>
<img width="894" alt="Screenshot 2025-01-07 at 1 59 39 PM"
src="https://github.com/user-attachments/assets/2a9fcee7-bcd0-4959-854c-e43daddbe7cf"
/>
  • Loading branch information
emma-sg authored Jan 7, 2025
1 parent 3b6f63f commit d6189ee
Showing 1 changed file with 53 additions and 2 deletions.
55 changes: 53 additions & 2 deletions frontend/src/components/orgs-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import type {
SlMenuItem,
} from "@shoelace-style/shoelace";
import { serialize } from "@shoelace-style/shoelace/dist/utilities/form.js";
import { css, html, nothing } from "lit";
import Fuse from "fuse.js";
import { css, html, nothing, type PropertyValues } from "lit";
import { customElement, property, query, state } from "lit/decorators.js";
import { when } from "lit/directives/when.js";

Expand Down Expand Up @@ -56,12 +57,62 @@ export class OrgsList extends BtrixElement {
@query("#orgDeleteButton")
private readonly orgDeleteButton?: SlButton | null;

// For fuzzy search:
private readonly fuse = new Fuse(this.orgList ?? [], {
keys: [
"id",
"name",
"slug",
"users.name",
"users.email",
"subscription.subId",
"subscription.planId",
],
useExtendedSearch: true,
});

@state()
private search = "";

protected willUpdate(changedProperties: PropertyValues<this>) {
if (changedProperties.has("orgList")) {
this.fuse.setCollection(this.orgList ?? []);
}
}

protected firstUpdated() {
this.fuse.setCollection(this.orgList ?? []);
}

render() {
if (this.skeleton) {
return this.renderSkeleton();
}

const orgs = this.search
? this.fuse.search(this.search).map(({ item }) => item)
: this.orgList;

return html`
<sl-input
value=${this.search}
clearable
size="small"
class="mb-6"
placeholder=${msg(
"Search all orgs by name, id, slug, users, and subscriptions",
)}
@sl-input=${(e: Event) => {
this.search = (e.target as SlInput).value.trim() || "";
}}
>
<sl-icon
name="search"
slot="prefix"
aria-hidden="true"
library="default"
></sl-icon
></sl-input>
<btrix-table>
<btrix-table-head class="mb-2">
<btrix-table-header-cell>
Expand All @@ -84,7 +135,7 @@ export class OrgsList extends BtrixElement {
</btrix-table-header-cell>
</btrix-table-head>
<btrix-table-body class="rounded border">
${this.orgList?.map(this.renderOrg)}
${orgs?.map(this.renderOrg)}
</btrix-table-body>
</btrix-table>
Expand Down

0 comments on commit d6189ee

Please sign in to comment.