diff --git a/frontend/src/features/collections/collections-grid-with-edit-dialog.ts b/frontend/src/features/collections/collections-grid-with-edit-dialog.ts index 3623aae104..29d0b8e296 100644 --- a/frontend/src/features/collections/collections-grid-with-edit-dialog.ts +++ b/frontend/src/features/collections/collections-grid-with-edit-dialog.ts @@ -33,6 +33,7 @@ export class CollectionsGridWithEditDialog extends BtrixElement { this.collectionBeingEdited = e.detail; }} > + diff --git a/frontend/src/features/collections/collections-grid.ts b/frontend/src/features/collections/collections-grid.ts index 4092cb5405..6660c3e20b 100644 --- a/frontend/src/features/collections/collections-grid.ts +++ b/frontend/src/features/collections/collections-grid.ts @@ -10,6 +10,7 @@ import { CollectionThumbnail } from "./collection-thumbnail"; import { SelectCollectionAccess } from "./select-collection-access"; import { BtrixElement } from "@/classes/BtrixElement"; +import { emptyMessage } from "@/layouts/emptyMessage"; import { textSeparator } from "@/layouts/separator"; import { RouteNamespace } from "@/routes"; import { CollectionAccess, type PublicCollection } from "@/types/collection"; @@ -60,16 +61,12 @@ export class CollectionsGrid extends BtrixElement { } if (!this.collections.length) { - return html` -
-

- - ${msg("No public collections yet.")} - -

- -
- `; + return emptyMessage({ + message: html` + ${msg("No public collections yet.")} + `, + actions: html``, + }); } const showActions = !this.navigate.isPublicPage && this.appState.isCrawler; diff --git a/frontend/src/pages/org/archived-items.ts b/frontend/src/pages/org/archived-items.ts index 58951dd6a7..a7e7f7cf36 100644 --- a/frontend/src/pages/org/archived-items.ts +++ b/frontend/src/pages/org/archived-items.ts @@ -28,9 +28,11 @@ import orgUploadsContext from "@/context/org-uploads"; import { ClipboardController } from "@/controllers/clipboard"; import PollTask from "@/controllers/poll"; import { SearchParamsValue } from "@/controllers/searchParamsValue"; +import { type BtrixUserGuideShowEvent } from "@/events/btrix-user-guide-show"; import { type BtrixChangeArchivedItemStateFilterEvent } from "@/features/archived-items/archived-item-state-filter"; import { CrawlStatus } from "@/features/archived-items/crawl-status"; import { type BtrixChangeQARatingFilterEvent } from "@/features/archived-items/qa-rating-filter"; +import { emptyMessage } from "@/layouts/emptyMessage"; import { pageHeader } from "@/layouts/pageHeader"; import type { APIPaginatedList, APIPaginationQuery } from "@/types/api"; import { UPLOAD_STATES, type CrawlState } from "@/types/crawlState"; @@ -1013,13 +1015,59 @@ export class CrawlsList extends BtrixElement { `; } - return html` -
-

- ${msg("No archived items yet.")} -

-
- `; + if (this.itemType === "crawl") { + return html` +
+

+ ${msg("No crawled items yet.")} +

+
+ `; + } + + const message = msg("Your org doesn’t have any archived items yet."); + const guideButton = html` { + this.dispatchEvent( + new CustomEvent( + "btrix-user-guide-show", + { + detail: { path: "archived-items" }, + bubbles: true, + composed: true, + }, + ), + ); + }} + > + + ${msg("Read Guide")} + `; + + if (this.isCrawler) { + return emptyMessage({ + classNames: tw`border-y`, + message, + detail: msg( + "Archived items are the result of a web archiving process, like crawl workflows.", + ), + actions: html` (this.isUploadingArchive = true)} + ?disabled=${isArchivingDisabled(this.org)} + > + + ${msg("Import Archived Item")} + + ${guideButton}`, + }); + } + + return emptyMessage({ + classNames: tw`border-y`, + message, + actions: guideButton, + }); } private async getArchivedItems( diff --git a/frontend/src/pages/org/browser-profiles-list.ts b/frontend/src/pages/org/browser-profiles-list.ts index deea2c19e4..bb76e60a98 100644 --- a/frontend/src/pages/org/browser-profiles-list.ts +++ b/frontend/src/pages/org/browser-profiles-list.ts @@ -20,6 +20,7 @@ import { parsePage, type PageChangeEvent } from "@/components/ui/pagination"; import type { BtrixChangeTagFilterEvent } from "@/components/ui/tag-filter/types"; import { ClipboardController } from "@/controllers/clipboard"; import { SearchParamsValue } from "@/controllers/searchParamsValue"; +import { type BtrixUserGuideShowEvent } from "@/events/btrix-user-guide-show"; import { originsWithRemainder } from "@/features/browser-profiles/templates/origins-with-remainder"; import { emptyMessage } from "@/layouts/emptyMessage"; import { pageHeader } from "@/layouts/pageHeader"; @@ -33,6 +34,7 @@ import { SortDirection as SortDirectionEnum } from "@/types/utils"; import { isApiError } from "@/utils/api"; import { isArchivingDisabled } from "@/utils/orgs"; import { toSearchItem, type SearchValues } from "@/utils/searchValues"; +import { tw } from "@/utils/tailwind"; const SORT_DIRECTIONS = ["asc", "desc"] as const; type SortDirection = (typeof SORT_DIRECTIONS)[number]; @@ -316,11 +318,17 @@ export class BrowserProfilesList extends BtrixElement { ` : this.renderEmpty()} `, + this.renderLoading, )} ${when(this.selectedProfile, this.renderDuplicateDialog)} `; }; + private readonly renderLoading = () => + html`
+ +
`; + private readonly renderDuplicateDialog = (profile: Profile) => { return html` { + this.dispatchEvent( + new CustomEvent( + "btrix-user-guide-show", + { + detail: { path: "browser-profiles" }, + bubbles: true, + composed: true, + }, + ), + ); + }} + > + + ${msg("Read Guide")} + `; if (this.isCrawler) { return emptyMessage({ + classNames: tw`border-y`, message, detail: msg( "Browser profiles let you crawl pages behind paywalls and logins.", @@ -376,11 +403,16 @@ export class BrowserProfilesList extends BtrixElement { ${msg("Create Browser Profile")} + ${guideButton} `, }); } - return emptyMessage({ message }); + return emptyMessage({ + classNames: tw`border-y`, + message, + actions: guideButton, + }); } private renderControls() { diff --git a/frontend/src/pages/org/collections-list.ts b/frontend/src/pages/org/collections-list.ts index 6360803ccd..0a1687a34b 100644 --- a/frontend/src/pages/org/collections-list.ts +++ b/frontend/src/pages/org/collections-list.ts @@ -13,13 +13,12 @@ import { when } from "lit/directives/when.js"; import debounce from "lodash/fp/debounce"; import queryString from "query-string"; -import type { SelectNewDialogEvent } from "."; - import { BtrixElement } from "@/classes/BtrixElement"; import { parsePage, type PageChangeEvent } from "@/components/ui/pagination"; import { WithSearchOrgContext } from "@/context/search-org/WithSearchOrgContext"; import { ClipboardController } from "@/controllers/clipboard"; import type { BtrixRequestOrgUpdate } from "@/events/btrix-request-org-update"; +import { type BtrixUserGuideShowEvent } from "@/events/btrix-user-guide-show"; import { SelectCollectionAccess } from "@/features/collections/select-collection-access"; import { emptyMessage } from "@/layouts/emptyMessage"; import { pageHeader } from "@/layouts/pageHeader"; @@ -299,7 +298,7 @@ export class CollectionsList extends WithSearchOrgContext(BtrixElement) { } private readonly renderLoading = () => - html`
+ html`
`; @@ -535,39 +534,51 @@ export class CollectionsList extends WithSearchOrgContext(BtrixElement) { } const message = msg("Your org doesn’t have any collections yet."); + const guideButton = html` { + this.dispatchEvent( + new CustomEvent( + "btrix-user-guide-show", + { + detail: { path: "collection" }, + bubbles: true, + composed: true, + }, + ), + ); + }} + > + + ${msg("Read Guide")} + `; - return html` - ${when( - this.isCrawler, - () => - emptyMessage({ - classNames: tw`border-y`, - message, - detail: msg( - "Collections let you easily organize, replay, and share multiple crawls.", - ), - actions: html` - { - this.dispatchEvent( - new CustomEvent("select-new-dialog", { - detail: "collection", - }) as SelectNewDialogEvent, - ); - }} - > - - ${msg("Create Collection")} - - `, - }), - () => - emptyMessage({ - classNames: tw`border-y`, - message, - }), - )} - `; + if (this.isCrawler) { + return emptyMessage({ + classNames: tw`border-y`, + message, + detail: msg( + "Collections let you easily organize, replay, and share multiple crawls.", + ), + actions: html` + { + this.openDialogName = "create"; + }} + > + + ${msg("Create New Collection")} + + ${guideButton} + `, + }); + } + + return emptyMessage({ + classNames: tw`border-y`, + message, + actions: guideButton, + }); }; private readonly renderItem = (col: Collection) => { diff --git a/frontend/src/pages/org/crawling.ts b/frontend/src/pages/org/crawling.ts index 824ec18b82..f41903d1bd 100644 --- a/frontend/src/pages/org/crawling.ts +++ b/frontend/src/pages/org/crawling.ts @@ -45,14 +45,18 @@ export class OrgCrawling extends BtrixElement { variant="primary" size="small" ?disabled=${this.org?.readOnly} - @click=${() => - this.navigate.to( - `${this.navigate.orgBasePath}/workflows/new`, - { - scopeType: - this.appState.userPreferences?.newWorkflowScopeType, - }, - )} + href=${`${this.navigate.orgBasePath}/workflows/new`} + @click=${(e: MouseEvent) => { + if (e.metaKey) { + return; + } + e.preventDefault(); + + this.navigate.to((e.target as HTMLAnchorElement).href, { + scopeType: + this.appState.userPreferences?.newWorkflowScopeType, + }); + }} > ${msg("New Workflow")}${this.collectionsView === CollectionGridView.Public - ? msg("No public collections yet.") - : msg("No collections yet.")} ${this.collections.value && this.collections.value.total > this.collections.value.items.length diff --git a/frontend/src/pages/org/workflows-list.ts b/frontend/src/pages/org/workflows-list.ts index 8eca9e854d..367c531a68 100644 --- a/frontend/src/pages/org/workflows-list.ts +++ b/frontend/src/pages/org/workflows-list.ts @@ -29,6 +29,7 @@ import activeCrawlsCountContext, { } from "@/context/active-crawls-count"; import { makeUpdateActiveCrawlsCountEvent } from "@/context/active-crawls-count/events"; import { SearchParamsValue } from "@/controllers/searchParamsValue"; +import { type BtrixUserGuideShowEvent } from "@/events/btrix-user-guide-show"; import { Action, type BtrixSelectActionEvent, @@ -40,6 +41,7 @@ import { WorkflowSearch, type SearchFields, } from "@/features/crawl-workflows/workflow-search"; +import { emptyMessage } from "@/layouts/emptyMessage"; import { WorkflowTab } from "@/routes"; import { deleteConfirmation } from "@/strings/ui"; import type { APIPaginatedList, APIPaginationQuery } from "@/types/api"; @@ -754,7 +756,7 @@ export class WorkflowsList extends BtrixElement {

${msg("No matching Workflows found.")}${msg("No matching workflows found.")}

- `; + const message = msg("Your org doesn’t have any crawl workflows yet."); + const guideButton = html` { + this.dispatchEvent( + new CustomEvent( + "btrix-user-guide-show", + { + detail: { path: "crawl-workflows" }, + bubbles: true, + composed: true, + }, + ), + ); + }} + > + + ${msg("Read Guide")} + `; + + if (this.appState.isCrawler) { + return emptyMessage({ + classNames: tw`border-y`, + message, + detail: msg("Schedule, run, and manage crawls with crawl workflows."), + actions: html` + { + if (e.metaKey) { + return; + } + e.preventDefault(); + + this.navigate.to((e.target as HTMLAnchorElement).href, { + scopeType: this.appState.userPreferences?.newWorkflowScopeType, + }); + }} + > + + ${msg("Create New Workflow")} + + ${guideButton} + `, + }); + } + + return emptyMessage({ + classNames: tw`border-y`, + message, + actions: guideButton, + }); } private renderLoading() { return html`
`;