Skip to content

Commit 03400ce

Browse files
committed
feat: Load first seed in profile (#2998)
- Replaces redundant "Go to workflow" menu item in browser profile workflow list with "Load Crawl Start URL". - Fixes not being able to open a workflow in a new tab (#3001) - Adds suggestions to site dropdown
1 parent f8901a9 commit 03400ce

File tree

6 files changed

+162
-54
lines changed

6 files changed

+162
-54
lines changed

frontend/src/features/browser-profiles/start-browser-dialog.ts

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { consume } from "@lit/context";
22
import { localized, msg } from "@lit/localize";
3+
import { Task } from "@lit/task";
34
import type {
45
SlButton,
56
SlChangeEvent,
@@ -11,6 +12,7 @@ import { html, nothing, type PropertyValues } from "lit";
1112
import { customElement, property, query, state } from "lit/decorators.js";
1213
import { ifDefined } from "lit/directives/if-defined.js";
1314
import { when } from "lit/directives/when.js";
15+
import queryString from "query-string";
1416

1517
import { BtrixElement } from "@/classes/BtrixElement";
1618
import type { Details } from "@/components/ui/details";
@@ -26,6 +28,7 @@ import {
2628
type OrgProxiesContext,
2729
} from "@/context/org-proxies";
2830
import type { Profile } from "@/types/crawler";
31+
import type { WorkflowSearchValues } from "@/types/workflow";
2932

3033
type StartBrowserEventDetail = {
3134
url?: string;
@@ -89,6 +92,48 @@ export class StartBrowserDialog extends BtrixElement {
8992
@query("#submit-button")
9093
private readonly submitButton?: SlButton | null;
9194

95+
// Get unique origins from workflow first seeds/crawl start URLs
96+
private readonly workflowOrigins = new Task(this, {
97+
task: async ([profile], { signal }) => {
98+
if (!profile) return null;
99+
100+
const query = queryString.stringify({ profileIds: profile.id });
101+
102+
try {
103+
const { firstSeeds } = await this.api.fetch<WorkflowSearchValues>(
104+
`/orgs/${this.orgId}/crawlconfigs/search-values?${query}`,
105+
{ signal },
106+
);
107+
108+
const profileUrls = profile.origins.map((origin) => new URL(origin));
109+
const originMap: { [url: string]: boolean } = {};
110+
111+
firstSeeds.forEach((seed) => {
112+
const seedUrl = new URL(seed);
113+
114+
// Only check domain names without www
115+
if (
116+
profileUrls.some((url) => {
117+
return (
118+
seedUrl.hostname.replace(/^www\./, "") ===
119+
url.hostname.replace(/^www\./, "")
120+
);
121+
})
122+
) {
123+
return;
124+
}
125+
126+
originMap[seedUrl.origin] = true;
127+
});
128+
129+
return Object.keys(originMap);
130+
} catch (e) {
131+
console.debug(e);
132+
}
133+
},
134+
args: () => [this.profile] as const,
135+
});
136+
92137
protected willUpdate(changedProperties: PropertyValues): void {
93138
if (changedProperties.has("initialUrl")) {
94139
this.loadUrl = this.initialUrl;
@@ -238,6 +283,15 @@ export class StartBrowserDialog extends BtrixElement {
238283
}
239284

240285
private readonly renderUrl = (profile: Profile) => {
286+
const option = (url: string) =>
287+
html`<sl-option
288+
class="part-[label]:overflow-hidden"
289+
value=${url}
290+
title=${url}
291+
>
292+
<div class="truncate">${url}</div>
293+
</sl-option>`;
294+
241295
return html`<sl-select
242296
name=${PRIMARY_SITE_FIELD_NAME}
243297
label=${msg("Primary Site")}
@@ -253,12 +307,21 @@ export class StartBrowserDialog extends BtrixElement {
253307
}
254308
}}
255309
>
310+
<sl-option value="">${msg("New Site")}</sl-option>
311+
<sl-divider></sl-divider>
256312
<sl-menu-label>${msg("Saved Sites")}</sl-menu-label>
257-
${profile.origins.map(
258-
(url) => html` <sl-option value=${url}>${url}</sl-option> `,
313+
${profile.origins.map(option)}
314+
${when(this.workflowOrigins.value, (seeds) =>
315+
seeds.length
316+
? html`
317+
<sl-divider></sl-divider>
318+
<sl-menu-label
319+
>${msg("Suggestions from Related Workflows")}</sl-menu-label
320+
>
321+
${seeds.map(option)}
322+
`
323+
: nothing,
259324
)}
260-
<sl-divider></sl-divider>
261-
<sl-option value="">${msg("New Site")}</sl-option>
262325
</sl-select>
263326
264327
<div class="mt-4">

frontend/src/features/crawl-workflows/workflow-list.ts

Lines changed: 69 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { ShareableNotice } from "./templates/shareable-notice";
2525

2626
import { BtrixElement } from "@/classes/BtrixElement";
2727
import type { OverflowDropdown } from "@/components/ui/overflow-dropdown";
28-
import { WorkflowTab } from "@/routes";
28+
import { OrgTab, WorkflowTab } from "@/routes";
2929
import { noData } from "@/strings/ui";
3030
import type { ListWorkflow } from "@/types/crawler";
3131
import { humanizeSchedule } from "@/utils/cron";
@@ -40,15 +40,15 @@ export type WorkflowColumnName =
4040
| "actions";
4141

4242
const columnWidths = {
43-
name: "minmax(18rem, 1fr)",
43+
// TODO Consolidate with table.stylesheet.css
44+
// https://github.com/webrecorder/browsertrix/issues/3001
45+
name: "[clickable-start] minmax(18rem, 1fr)",
4446
"latest-crawl": "minmax(15rem, 18rem)",
4547
"total-crawls": "minmax(6rem, 9rem)",
4648
modified: "minmax(12rem, 15rem)",
47-
actions: "3rem",
49+
actions: "[clickable-end] 3rem",
4850
} as const satisfies Record<WorkflowColumnName, string>;
4951

50-
// postcss-lit-disable-next-line
51-
const mediumBreakpointCss = css`30rem`;
5252
// postcss-lit-disable-next-line
5353
const largeBreakpointCss = css`60rem`;
5454
// postcss-lit-disable-next-line
@@ -65,11 +65,6 @@ const rowCss = css`
6565
right: 0;
6666
}
6767
68-
@media only screen and (min-width: ${mediumBreakpointCss}) {
69-
.row {
70-
grid-template-columns: repeat(2, 1fr);
71-
}
72-
}
7368
@media only screen and (min-width: ${largeBreakpointCss}) {
7469
.row {
7570
grid-template-columns: var(--btrix-workflow-list-columns);
@@ -252,6 +247,43 @@ export class WorkflowListItem extends BtrixElement {
252247
border-left: 1px solid var(--sl-panel-border-color);
253248
}
254249
}
250+
251+
/*
252+
* TODO Consolidate with table.stylesheet.css
253+
* https://github.com/webrecorder/browsertrix/issues/3001
254+
*/
255+
.rowClickTarget--cell {
256+
display: grid;
257+
grid-template-columns: subgrid;
258+
white-space: nowrap;
259+
overflow: hidden;
260+
}
261+
262+
.rowClickTarget {
263+
max-width: 100%;
264+
}
265+
266+
.col sl-tooltip > *,
267+
.col btrix-popover > *,
268+
.col btrix-overflow-dropdown {
269+
/* Place above .rowClickTarget::after overlay */
270+
z-index: 10;
271+
position: relative;
272+
}
273+
274+
.rowClickTarget::after {
275+
content: "";
276+
display: block;
277+
position: absolute;
278+
inset: 0;
279+
grid-column: clickable-start / clickable-end;
280+
}
281+
282+
.rowClickTarget:focus-visible {
283+
outline: var(--sl-focus-ring);
284+
outline-offset: -0.25rem;
285+
border-radius: 0.5rem;
286+
}
255287
`,
256288
];
257289

@@ -271,13 +303,18 @@ export class WorkflowListItem extends BtrixElement {
271303
@query("btrix-overflow-dropdown")
272304
dropdownMenu!: OverflowDropdown;
273305

306+
@query("a")
307+
private readonly anchor?: HTMLAnchorElement | null;
308+
274309
private readonly columnTemplate = {
275-
name: () =>
276-
html`<div class="col">
277-
<div class="detail url items-center truncate">
310+
name: () => {
311+
const href = `/orgs/${this.orgSlugState}/${OrgTab.Workflows}/${this.workflow?.id}/${this.workflow?.lastCrawlState?.startsWith("failed") ? WorkflowTab.Logs : WorkflowTab.LatestCrawl}`;
312+
313+
return html`<div class="col rowClickTarget--cell">
314+
<a class="detail url rowClickTarget items-center truncate" href=${href}>
278315
${when(this.workflow?.shareable, ShareableNotice)}
279316
${this.safeRender(this.renderName)}
280-
</div>
317+
</a>
281318
<div class="desc truncate">
282319
${this.safeRender((workflow) => {
283320
if (workflow.schedule) {
@@ -291,7 +328,8 @@ export class WorkflowListItem extends BtrixElement {
291328
return msg("---");
292329
})}
293330
</div>
294-
</div>`,
331+
</div>`;
332+
},
295333
"latest-crawl": () =>
296334
html`<div class="col">${this.safeRender(this.renderLatestCrawl)}</div>`,
297335
"total-crawls": () =>
@@ -358,20 +396,7 @@ export class WorkflowListItem extends BtrixElement {
358396
} satisfies Record<WorkflowColumnName, () => TemplateResult>;
359397

360398
render() {
361-
return html`<div
362-
class="item row"
363-
role="button"
364-
@click=${async (e: MouseEvent) => {
365-
if (e.target === this.dropdownMenu) {
366-
return;
367-
}
368-
e.preventDefault();
369-
await this.updateComplete;
370-
const failedStates = ["failed", "failed_not_logged_in"];
371-
const href = `/orgs/${this.orgSlugState}/workflows/${this.workflow?.id}/${failedStates.includes(this.workflow?.lastCrawlState || "") ? WorkflowTab.Logs : WorkflowTab.LatestCrawl}`;
372-
this.navigate.to(href);
373-
}}
374-
>
399+
return html`<div class="item row">
375400
${this.columns
376401
? this.columns.map((col) => this.columnTemplate[col]())
377402
: Object.values(this.columnTemplate).map((render) => render())}
@@ -480,7 +505,7 @@ export class WorkflowListItem extends BtrixElement {
480505

481506
return html`
482507
<sl-tooltip hoist placement="bottom" ?disabled=${!tooltipContent}>
483-
<div>
508+
<div class="w-max" @click=${this.redirectEventToAnchor}>
484509
<div class="detail">${status}</div>
485510
<div class="desc duration">${duration}</div>
486511
</div>
@@ -495,7 +520,7 @@ export class WorkflowListItem extends BtrixElement {
495520

496521
return html`
497522
<sl-tooltip hoist placement="bottom">
498-
<div>
523+
<div class="w-max" @click=${this.redirectEventToAnchor}>
499524
<div class="detail truncate">
500525
${workflow.modifiedByName
501526
? html`<btrix-user-chip
@@ -548,6 +573,19 @@ export class WorkflowListItem extends BtrixElement {
548573
>${nameSuffix}
549574
`;
550575
};
576+
577+
/*
578+
* TODO Remove when refactored to `btrix-table`
579+
* https://github.com/webrecorder/browsertrix/issues/3001
580+
*/
581+
private readonly redirectEventToAnchor = (e: MouseEvent) => {
582+
if (!e.defaultPrevented) {
583+
const newEvent = new MouseEvent(e.type, e);
584+
e.stopPropagation();
585+
586+
this.anchor?.dispatchEvent(newEvent);
587+
}
588+
};
551589
}
552590

553591
@customElement("btrix-workflow-list")

frontend/src/pages/org/browser-profiles/profile.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,17 @@ export class BrowserProfilesProfilePage extends BtrixElement {
669669
(workflow) =>
670670
html`<btrix-workflow-list-item .workflow=${workflow}>
671671
<sl-menu slot="menu">
672+
<sl-menu-item
673+
@click=${() =>
674+
void this.openBrowser({ url: workflow.firstSeed })}
675+
>
676+
<sl-icon
677+
slot="prefix"
678+
name="window-fullscreen"
679+
></sl-icon>
680+
${msg("Load Crawl Start URL")}
681+
</sl-menu-item>
682+
<sl-divider></sl-divider>
672683
${when(
673684
this.appState.isCrawler,
674685
() => html`
@@ -679,19 +690,8 @@ export class BrowserProfilesProfilePage extends BtrixElement {
679690
<sl-icon name="gear" slot="prefix"></sl-icon>
680691
${msg("Edit Workflow Settings")}
681692
</btrix-menu-item-link>
682-
<sl-divider></sl-divider>
683693
`,
684694
)}
685-
<btrix-menu-item-link
686-
href="${this.navigate
687-
.orgBasePath}/${OrgTab.Workflows}/${workflow.id}"
688-
>
689-
<sl-icon
690-
name="arrow-return-right"
691-
slot="prefix"
692-
></sl-icon>
693-
${msg("Go to Workflow")}
694-
</btrix-menu-item-link>
695695
</sl-menu>
696696
</btrix-workflow-list-item>`,
697697
)}

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { WorkflowTab } from "@/routes";
3838
import { deleteConfirmation } from "@/strings/ui";
3939
import type { APIPaginatedList, APIPaginationQuery } from "@/types/api";
4040
import { type CrawlState } from "@/types/crawlState";
41-
import { type StorageSeedFile } from "@/types/workflow";
41+
import type { StorageSeedFile, WorkflowSearchValues } from "@/types/workflow";
4242
import { isApiError } from "@/utils/api";
4343
import { settingsForDuplicate } from "@/utils/crawl-workflows/settingsForDuplicate";
4444
import { renderName } from "@/utils/crawler";
@@ -994,12 +994,7 @@ export class WorkflowsList extends BtrixElement {
994994

995995
private async fetchConfigSearchValues() {
996996
try {
997-
const data: {
998-
crawlIds: string[];
999-
names: string[];
1000-
descriptions: string[];
1001-
firstSeeds: string[];
1002-
} = await this.api.fetch(
997+
const data = await this.api.fetch<WorkflowSearchValues>(
1003998
`/orgs/${this.orgId}/crawlconfigs/search-values`,
1004999
);
10051000

frontend/src/theme.stylesheet.css

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,16 @@
237237
}
238238

239239
/* Adjust menu item hover and focus styles */
240-
sl-menu-item,
240+
sl-option:not([aria-selected="true"]):not(:disabled),
241+
sl-menu-item:not([disabled]),
241242
btrix-menu-item-link {
242243
@apply part-[base]:text-neutral-700 part-[base]:hover:bg-cyan-50/50 part-[base]:hover:text-cyan-700 part-[base]:focus-visible:bg-cyan-50/50;
243244
}
244245

246+
sl-option[aria-selected="true"] {
247+
@apply part-[base]:bg-cyan-50 part-[base]:text-cyan-700;
248+
}
249+
245250
/* Add menu item variants */
246251
.menu-item-success {
247252
@apply part-[base]:text-success part-[base]:hover:bg-success-50 part-[base]:hover:text-success-700 part-[base]:focus-visible:bg-success-50;

frontend/src/types/workflow.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,10 @@ export type StorageSeedFile = StorageFile & {
1616
firstSeed: string;
1717
seedCount: number;
1818
};
19+
20+
export type WorkflowSearchValues = {
21+
crawlIds: string[];
22+
names: string[];
23+
descriptions: string[];
24+
firstSeeds: string[];
25+
};

0 commit comments

Comments
 (0)