Skip to content

Commit 98d8218

Browse files
authored
Fix superadmin running crawls views (#846)
- Updates superadmin "Running Crawls" to show active crawls (starting, waiting, running, stopping) and sort by start by default - Navigates to crawl workflow watch view on clicking crawl item - Adds "Copy Crawl ID" to crawl actions for easy paste into "Jump to crawl" - Navigates to crawl workflow watch when jumping to crawl
1 parent d8b36c0 commit 98d8218

File tree

5 files changed

+101
-34
lines changed

5 files changed

+101
-34
lines changed

frontend/src/components/crawl-list.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { RelativeDuration } from "./relative-duration";
2727
import type { Crawl } from "../types/crawler";
2828
import { srOnly, truncate, dropdown } from "../utils/css";
2929
import type { NavigateEvent } from "../utils/LiteElement";
30+
import { isActive } from "../utils/crawler";
3031

3132
const mediumBreakpointCss = css`30rem`;
3233
const largeBreakpointCss = css`60rem`;
@@ -219,12 +220,13 @@ export class CrawlListItem extends LitElement {
219220
}
220221

221222
renderRow() {
223+
const hash = this.crawl && isActive(this.crawl.state) ? "#watch" : "";
222224
return html`<a
223225
class="item row"
224226
role="button"
225227
href=${`${this.baseUrl || `/orgs/${this.crawl?.oid}/artifacts/crawl`}/${
226228
this.crawl?.id
227-
}`}
229+
}${hash}`}
228230
@click=${async (e: MouseEvent) => {
229231
e.preventDefault();
230232
await this.updateComplete;

frontend/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -704,8 +704,8 @@ export class App extends LiteElement {
704704
<form
705705
@submit=${(e: any) => {
706706
e.preventDefault();
707-
const id = new FormData(e.target).get("crawlId");
708-
this.navigate(`/crawls/crawl/${id}`);
707+
const id = new FormData(e.target).get("crawlId") as string;
708+
this.navigate(`/crawls/crawl/${id}#watch`);
709709
e.target.closest("sl-dropdown").hide();
710710
}}
711711
>

frontend/src/pages/crawls.ts

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,29 @@ import { msg, localized, str } from "@lit/localize";
44
import type { AuthState } from "../utils/AuthService";
55
import LiteElement, { html } from "../utils/LiteElement";
66
import { needLogin } from "../utils/auth";
7+
import type { Crawl } from "../types/crawler";
78
import { ROUTES } from "../routes";
8-
import "./org/crawl-detail";
9+
import "./org/workflow-detail";
910
import "./org/crawls-list";
1011

1112
@needLogin
1213
@localized()
1314
export class Crawls extends LiteElement {
1415
@property({ type: Object })
15-
authState?: AuthState;
16+
authState!: AuthState;
1617

1718
@property({ type: String })
1819
crawlId?: string;
1920

21+
@state()
22+
private crawl?: Crawl;
23+
24+
willUpdate(changedProperties: Map<string, any>) {
25+
if (changedProperties.has("crawlId") && this.crawlId) {
26+
this.fetchWorkflowId();
27+
}
28+
}
29+
2030
render() {
2131
return html` <div
2232
class="w-full max-w-screen-lg mx-auto px-3 py-4 box-border"
@@ -26,23 +36,44 @@ export class Crawls extends LiteElement {
2636
}
2737

2838
private renderDetail() {
39+
if (!this.crawl) return;
40+
2941
return html`
30-
<btrix-crawl-detail
42+
<btrix-workflow-detail
3143
.authState=${this.authState!}
32-
crawlId=${this.crawlId!}
33-
crawlsBaseUrl=${ROUTES.crawls}
34-
crawlsAPIBaseUrl="/orgs/all/crawls"
35-
showOrgLink
36-
></btrix-crawl-detail>
44+
orgId=${this.crawl.oid}
45+
workflowId=${this.crawl.cid}
46+
initialActivePanel="watch"
47+
isCrawler
48+
></btrix-workflow-detail>
3749
`;
3850
}
3951

4052
private renderList() {
4153
return html`<btrix-crawls-list
42-
.authState=${this.authState!}
54+
.authState=${this.authState}
4355
crawlsBaseUrl=${ROUTES.crawls}
4456
crawlsAPIBaseUrl="/orgs/all/crawls"
57+
isCrawler
58+
isAdminView
4559
shouldFetch
4660
></btrix-crawls-list>`;
4761
}
62+
63+
private async fetchWorkflowId() {
64+
try {
65+
this.crawl = await this.getCrawl();
66+
} catch (e) {
67+
console.error(e);
68+
}
69+
}
70+
71+
private async getCrawl(): Promise<Crawl> {
72+
const data: Crawl = await this.apiFetch(
73+
`/orgs/all/crawls/${this.crawlId}/replay.json`,
74+
this.authState!
75+
);
76+
77+
return data;
78+
}
4879
}

frontend/src/pages/org/crawls-list.ts

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type { AuthState } from "../../utils/AuthService";
1818
import LiteElement, { html } from "../../utils/LiteElement";
1919
import type { Crawl, CrawlState, Workflow, WorkflowParams } from "./types";
2020
import type { APIPaginatedList, APIPaginationQuery } from "../../types/api";
21-
import { isActive } from "../../utils/crawler";
21+
import { isActive, activeCrawlStates } from "../../utils/crawler";
2222

2323
type Crawls = APIPaginatedList & {
2424
items: Crawl[];
@@ -78,6 +78,7 @@ export class CrawlsList extends LiteElement {
7878
firstSeed: msg("Crawl Start URL"),
7979
cid: msg("Workflow ID"),
8080
};
81+
8182
@property({ type: Object })
8283
authState!: AuthState;
8384

@@ -87,6 +88,11 @@ export class CrawlsList extends LiteElement {
8788
@property({ type: Boolean })
8889
isCrawler!: boolean;
8990

91+
// TODO better handling of using same crawls-list
92+
// component between superadmin view and regular view
93+
@property({ type: Boolean })
94+
isAdminView = false;
95+
9096
// e.g. `/org/${this.orgId}/crawls`
9197
@property({ type: String })
9298
crawlsBaseUrl!: string;
@@ -103,7 +109,7 @@ export class CrawlsList extends LiteElement {
103109
shouldFetch?: boolean;
104110

105111
@state()
106-
private lastFetched?: number;
112+
private crawlStates: CrawlState[] = finishedCrawlStates;
107113

108114
@state()
109115
private crawls?: Crawls;
@@ -166,6 +172,15 @@ export class CrawlsList extends LiteElement {
166172
}
167173

168174
protected willUpdate(changedProperties: Map<string, any>) {
175+
if (changedProperties.has("isAdminView") && this.isAdminView === true) {
176+
// TODO better handling of using same crawls-list
177+
// component between superadmin view and regular view
178+
this.crawlStates = activeCrawlStates;
179+
this.orderBy = {
180+
field: "started",
181+
direction: sortableFields["started"].defaultDirection!,
182+
};
183+
}
169184
if (
170185
changedProperties.has("shouldFetch") ||
171186
changedProperties.get("crawlsBaseUrl") ||
@@ -199,7 +214,10 @@ export class CrawlsList extends LiteElement {
199214
changedProperties.has("crawlsBaseUrl") ||
200215
changedProperties.has("crawlsAPIBaseUrl")
201216
) {
202-
this.fetchConfigSearchValues();
217+
// TODO add back when API supports `orgs/all/crawlconfigs`
218+
if (!this.isAdminView) {
219+
this.fetchConfigSearchValues();
220+
}
203221
}
204222
}
205223

@@ -223,7 +241,11 @@ export class CrawlsList extends LiteElement {
223241
<main>
224242
<header class="contents">
225243
<div class="flex w-full h-8 mb-4">
226-
<h1 class="text-xl font-semibold">${msg("Finished Crawls")}</h1>
244+
<h1 class="text-xl font-semibold">
245+
${this.isAdminView
246+
? msg("Running Crawls")
247+
: msg("Finished Crawls")}
248+
</h1>
227249
</div>
228250
<div
229251
class="sticky z-10 mb-3 top-2 p-4 bg-neutral-50 border rounded-lg"
@@ -266,7 +288,7 @@ export class CrawlsList extends LiteElement {
266288
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-[minmax(0,100%)_fit-content(100%)_fit-content(100%)] gap-x-2 gap-y-2 items-center"
267289
>
268290
<div class="col-span-1 md:col-span-2 lg:col-span-1">
269-
${this.renderSearch()}
291+
${when(!this.isAdminView, () => this.renderSearch())}
270292
</div>
271293
<div class="flex items-center">
272294
<div class="text-neutral-500 mx-2">${msg("View:")}</div>
@@ -277,7 +299,9 @@ export class CrawlsList extends LiteElement {
277299
pill
278300
multiple
279301
max-tags-visible="1"
280-
placeholder=${msg("Finished Crawls")}
302+
placeholder=${this.isAdminView
303+
? msg("All Active Crawls")
304+
: msg("Finished Crawls")}
281305
@sl-change=${async (e: CustomEvent) => {
282306
const value = (e.target as SlSelect).value as CrawlState[];
283307
await this.updateComplete;
@@ -287,7 +311,7 @@ export class CrawlsList extends LiteElement {
287311
};
288312
}}
289313
>
290-
${finishedCrawlStates.map(this.renderStatusMenuItem)}
314+
${this.crawlStates.map(this.renderStatusMenuItem)}
291315
</sl-select>
292316
</div>
293317
@@ -448,7 +472,7 @@ export class CrawlsList extends LiteElement {
448472
if (!this.crawls) return;
449473

450474
return html`
451-
<btrix-crawl-list>
475+
<btrix-crawl-list baseUrl=${this.isAdminView ? "/crawls/crawl" : ""}>
452476
${this.crawls.items.map(this.renderCrawlItem)}
453477
</btrix-crawl-list>
454478
@@ -515,6 +539,10 @@ export class CrawlsList extends LiteElement {
515539
<sl-icon name="copy-code" library="app" slot="prefix"></sl-icon>
516540
${msg("Copy Workflow ID")}
517541
</sl-menu-item>
542+
<sl-menu-item @click=${() => CopyButton.copyToClipboard(crawl.id)}>
543+
<sl-icon name="copy-code" library="app" slot="prefix"></sl-icon>
544+
${msg("Copy Crawl ID")}
545+
</sl-menu-item>
518546
<sl-menu-item
519547
@click=${() => CopyButton.copyToClipboard(crawl.tags.join(","))}
520548
?disabled=${!crawl.tags.length}
@@ -639,7 +667,7 @@ export class CrawlsList extends LiteElement {
639667
}
640668

641669
private async getCrawls(queryParams?: APIPaginationQuery): Promise<Crawls> {
642-
const state = this.filterBy.state || finishedCrawlStates;
670+
const state = this.filterBy.state || this.crawlStates;
643671
const query = queryString.stringify(
644672
{
645673
...this.filterBy,
@@ -666,7 +694,6 @@ export class CrawlsList extends LiteElement {
666694
);
667695

668696
this.getCrawlsController = null;
669-
this.lastFetched = Date.now();
670697

671698
return data;
672699
}

frontend/src/pages/org/workflow-detail.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { HTMLTemplateResult, TemplateResult } from "lit";
22
import { state, property } from "lit/decorators.js";
33
import { when } from "lit/directives/when.js";
4+
import { until } from "lit/directives/until.js";
45
import { ifDefined } from "lit/directives/if-defined.js";
56
import { msg, localized, str } from "@lit/localize";
67
import queryString from "query-string";
@@ -424,16 +425,22 @@ export class WorkflowDetail extends LiteElement {
424425
<btrix-tab-panel name="artifacts"
425426
>${this.renderArtifacts()}</btrix-tab-panel
426427
>
427-
<btrix-tab-panel name="watch"
428-
>${when(this.activePanel === "watch", () =>
429-
this.currentCrawlId
430-
? html` <div class="border rounded-lg py-2 mb-5 h-14">
431-
${this.renderCurrentCrawl()}
432-
</div>
433-
${this.renderWatchCrawl()}`
434-
: this.renderInactiveWatchCrawl()
435-
)}</btrix-tab-panel
436-
>
428+
<btrix-tab-panel name="watch">
429+
${until(
430+
this.getWorkflowPromise?.then(
431+
() => html`
432+
${when(this.activePanel === "watch", () =>
433+
this.currentCrawlId
434+
? html` <div class="border rounded-lg py-2 mb-5 h-14">
435+
${this.renderCurrentCrawl()}
436+
</div>
437+
${this.renderWatchCrawl()}`
438+
: this.renderInactiveWatchCrawl()
439+
)}
440+
`
441+
)
442+
)}
443+
</btrix-tab-panel>
437444
<btrix-tab-panel name="settings">
438445
${this.renderSettings()}
439446
</btrix-tab-panel>
@@ -496,7 +503,7 @@ export class WorkflowDetail extends LiteElement {
496503
return html`
497504
<a
498505
slot="nav"
499-
href=${`/orgs/${this.orgId}/workflows/crawl/${this.workflow?.id}#${tabName}`}
506+
href=${`${window.location.pathname}#${tabName}`}
500507
class="block font-medium rounded-sm mb-2 mr-2 p-2 transition-all ${className}"
501508
aria-selected=${isActive}
502509
aria-disabled=${disabled}
@@ -806,7 +813,7 @@ export class WorkflowDetail extends LiteElement {
806813
${msg(
807814
html`Crawl is currently running.
808815
<a
809-
href="${`/orgs/${this.orgId}/workflows/crawl/${this.workflow?.id}#watch`}"
816+
href="${`${window.location.pathname}#watch`}"
810817
class="underline hover:no-underline"
811818
>Watch Crawl Progress</a
812819
>`

0 commit comments

Comments
 (0)