diff --git a/frontend/.storybook/main.ts b/frontend/.storybook/main.ts
index 923e3eb923..10449c3728 100644
--- a/frontend/.storybook/main.ts
+++ b/frontend/.storybook/main.ts
@@ -100,6 +100,12 @@ const config: StorybookConfig = {
}),
);
+ // Add support for additional media types
+ config.module?.rules?.push({
+ test: /\.(webp|avif)(\?v=\d+\.\d+\.\d+)?$/,
+ type: "asset/resource",
+ });
+
// Watch for changes to custom-elements.json to re-render element
// attributes whenever the custom elements manifest is generated
(config as WebpackConfiguration).devServer = {
diff --git a/frontend/docs/docs/stylesheets/extra.css b/frontend/docs/docs/stylesheets/extra.css
index f07f9dd85b..fac5a13483 100644
--- a/frontend/docs/docs/stylesheets/extra.css
+++ b/frontend/docs/docs/stylesheets/extra.css
@@ -728,6 +728,7 @@ h5 {
/* .btrix-button is used to indicate a button in the application and is not interactive */
+.btrix-button,
.md-button.btrix-button {
vertical-align: baseline;
border-radius: 0.25rem;
@@ -747,6 +748,10 @@ h5 {
color: white;
}
+.btrix-button:not(.md-button) {
+ border: 1px solid var(--wr-border-color);
+}
+
.md-typeset [type="checkbox"]:checked + .task-list-indicator::before {
background-color: var(--wr-task-list-indicator-bg-color);
}
diff --git a/frontend/docs/docs/user-guide/getting-started.md b/frontend/docs/docs/user-guide/getting-started.md
index 2d1dfc9c7e..a089652c17 100644
--- a/frontend/docs/docs/user-guide/getting-started.md
+++ b/frontend/docs/docs/user-guide/getting-started.md
@@ -26,7 +26,7 @@ You likely have access already if:
- [x] You joined an existing org and were given “crawler” permissions.
- [x] You are the admin of a self-hosted instance.
-Check if you have access by logging in. If you see a [+ Create New...](#accessing-your-dashboard){ .md-button .md-button--primary .btrix-button } button in the dashboard, you’re able to start a site crawl. If you don’t see this button and think that you should, contact your org administrator to update your permissions.
+Check if you have access by logging in. If you see a **+ Create New...**{ .md-button .md-button--primary .btrix-button } button in the dashboard, you’re able to start a site crawl. If you don’t see this button and think that you should, contact your org administrator to update your permissions.
## Choose what to archive
@@ -53,11 +53,11 @@ Choose one of the following options to tailor this guide to what you’d like to
## Starting the crawl
-When you log in, the first page you see is the [org dashboard](overview.md){ data-preview }. If you’ve navigated away to another page, navigate back to **Dashboard**.
+When you log in, the first page you see is the org dashboard. If you’ve navigated away to another page, navigate back to **Dashboard**.
-You will create a [crawl workflow](crawl-workflows.md){ data-preview } to set up the crawl.
+You will create a [crawl workflow](concepts.md#crawl-workflow){ data-preview } to set up the crawl.
-1. Tap the [+ Create New...](#accessing-your-dashboard){ .md-button .md-button--primary .btrix-button } button and select **Crawl Workflow**.
+1. Tap the **+ Create New...**{ .btrix-button } button and select **Crawl Workflow**.
1. Select the [Crawl Scope](workflow-setup.md#crawl-scope){ data-preview } that best fits what you intend to archive:
@@ -101,7 +101,7 @@ You will create a [crawl workflow](crawl-workflows.md){ data-preview } to set up
1. Scroll down and open the **Crawl Limits** section.
1. Set a **Crawl Time Limit** and/or **Crawl Size Limit** according to what is reasonable within your org quotas.
-1. When you’re finished entering workflow settings, tap **Run Crawl**{ .md-button .md-button--primary .btrix-button }.
+1. When you’re finished entering workflow settings, tap **Run Crawl**{ .btrix-button }.
You should now see your new crawl workflow running. Give the crawler a few moments to warm up, and then watch as it crawls the webpage!
diff --git a/frontend/docs/docs/user-guide/index.md b/frontend/docs/docs/user-guide/index.md
index e9cf0bccc7..a2eec2d6c1 100644
--- a/frontend/docs/docs/user-guide/index.md
+++ b/frontend/docs/docs/user-guide/index.md
@@ -77,8 +77,8 @@ The user guide documents features, terminology, and settings in the Browsertrix
- [Change your email](user-settings.md)
- [Change your password](change-password.md#change-password)
- [Reset your password](change-password.md#reset-password)
- - [Choose your preferred language](user-settings.md)
- - [Change how your name appears to team members](user-settings.md)
+ - [Choose your preferred language](user-settings.md#language)
+ - [Change how your name appears to team members](user-settings.md#display-name)
diff --git a/frontend/docs/docs/user-guide/navigation.md b/frontend/docs/docs/user-guide/navigation.md
index 98ae50eae9..400bc84dca 100644
--- a/frontend/docs/docs/user-guide/navigation.md
+++ b/frontend/docs/docs/user-guide/navigation.md
@@ -20,6 +20,14 @@ You can find links to navigate your org near the top of each page, directly belo
---
+## Navigate to the User Guide
+
+You can access guides from any org page by tapping the **User Guide** button at the top of the page. The user guide will remain open as you navigate between org pages.
+
+By default, the user guide will open to the side so that you can use Browsertrix and view guides at the same time. To open the user guide in a new tab in your browser, activate the **User Guide** menu and select the _Open in New Tab_ option.
+
+---
+
## Navigate to Active Crawls
If any crawls in your org are active—i.e. visiting pages, waiting to visit pages, or temporarily paused—a link indicating the total number of active crawl runs will appear at the top of every page, next to the **User Guide** button.
diff --git a/frontend/docs/mkdocs.yml b/frontend/docs/mkdocs.yml
index eb3315ed27..972f56ab73 100644
--- a/frontend/docs/mkdocs.yml
+++ b/frontend/docs/mkdocs.yml
@@ -27,6 +27,7 @@ theme:
- content.tooltips
- content.tabs.link
- search.suggest
+ - search.share
palette:
- media: "(prefers-color-scheme: light)"
scheme: webrecorder-light
diff --git a/frontend/src/__mocks__/api/collections.js b/frontend/src/__mocks__/api/collections.js
new file mode 100644
index 0000000000..59293207ec
--- /dev/null
+++ b/frontend/src/__mocks__/api/collections.js
@@ -0,0 +1,181 @@
+export default {
+ total: 4,
+ page: 1,
+ pageSize: 16,
+ items: [
+ {
+ id: "fake-fc38-4eab-b857-705b6b1516c9",
+ name: "Webrecorder Websites",
+ slug: "webrecorder-websites",
+ oid: "x_example_org_id_x",
+ description:
+ "# Webrecorder Archives\n\nThis collection powers all our archives of our current website [webrecorder.net](https://webrecorder.net) including our [404 page](https://webrecorder.net/404) and [ReplayWeb.page demo](https://webrecorder.net/replaywebpage).\n\nThis collection also highlights the progress we've made on Browsertrix throughout the years; compare the archived [browsertrix.cloud](https://app.browsertrix.com/explore/webrecorder/collections/webrecorder-archives#url=https%3A%2F%2Fbrowsertrix.cloud%2F) landing page to our live [webrecorder.net/browsertrix](https://webrecorder.net/browsertrix/) page!",
+ caption: "Webrecorder’s web presence throughout the years.",
+ created: null,
+ modified: "2026-09-03T20:20:16Z",
+ lastStatsUpdateStarted: "2026-09-03T20:20:23Z",
+ crawlCount: 22,
+ pageCount: 1470,
+ uniquePageCount: 341,
+ totalSize: 1172271420,
+ dateEarliest: "2023-09-28T16:55:40Z",
+ dateLatest: "2026-09-03T20:19:53.166000Z",
+ tags: ["webrecorder"],
+ access: "public",
+ homeUrl: "https://webrecorder.net/",
+ homeUrlTs: "2026-06-03T19:20:04.468000Z",
+ homeUrlPageId: "acda8d04-0c53-4e7f-b8fa-086eab3127b2",
+ resources: [],
+ thumbnail: {
+ name: "fdc12a92-24c8-4c2d-aad1-9be73c4a5d31/images/-thumbnail-29b41745-fc38-4eab-b857-705b6b1516c9-et7retez.jpeg",
+ path: "https://files.browsertrix.com/crawls/fdc12a92-24c8-4c2d-aad1-9be73c4a5d31/images/-thumbnail-29b41745-fc38-4eab-b857-705b6b1516c9-et7retez.jpeg",
+ hash: "fe0e35c3ceb76428766d169c9468a3c22b4a48e9227db62eee659fc2296f2ac0",
+ size: 26451,
+ mime: "image/jpeg",
+ originalFilename:
+ "page-thumbnail_acda8d04-0c53-4e7f-b8fa-086eab3127b2.jpeg",
+ userid: "fake-8b3e-449e-b8b7-8ee725a9e9a5",
+ userName: "User Name",
+ created: "2026-06-03T19:31:31Z",
+ },
+ thumbnailSource: {
+ url: "https://webrecorder.net/",
+ urlTs: "2026-06-03T19:20:04.468000Z",
+ urlPageId: "acda8d04-0c53-4e7f-b8fa-086eab3127b2",
+ },
+ defaultThumbnailName: null,
+ allowPublicDownload: true,
+ initialPages: [],
+ preloadResources: [],
+ pagesQueryUrl: "",
+ downloadUrl: null,
+ topPageHosts: [
+ { host: "webrecorder.net", count: 1354 },
+ { host: "docs.browsertrix.cloud", count: 34 },
+ { host: "replayweb.page", count: 26 },
+ { host: "docs.browsertrix.com", count: 22 },
+ { host: "crawler.docs.browsertrix.com", count: 11 },
+ { host: "browsertrix.cloud", count: 8 },
+ { host: "twitter.com", count: 6 },
+ { host: "t.co", count: 2 },
+ { host: "browsertrix.com", count: 2 },
+ { host: "oembed.link", count: 1 },
+ ],
+ indexLastSavedAt: "2026-09-03T20:24:40Z",
+ indexState: "idle",
+ indexStats: {
+ totalUrls: 10808,
+ dupeUrls: 4673,
+ conservedSize: 442208093,
+ totalCrawls: 22,
+ totalCrawlSize: 1172271420,
+ removedCrawls: 0,
+ removedCrawlSize: 0,
+ updateProgress: 1.0,
+ uniqueHashes: 2725,
+ estimatedRedundantSize: 386852193,
+ },
+ runningUpdatesCount: 0,
+ },
+ {
+ id: "fake-ef0b-4bcf-a03a-004b5a721c35",
+ name: "Webrecorder Social Media",
+ slug: "webrecorder-social-media",
+ oid: "x_example_org_id_x",
+ description:
+ "# Webrecorder Social Media\n\nThis collection includes Browsertrix captures of Webrecorder's social media presence, demonstrating the effectiveness of Browsertrix's [built-in automated page behaviors](https://docs.browsertrix.com/user-guide/behaviors/#platform-specific) and [smart scoping](https://docs.browsertrix.com/user-guide/workflow-setup/#use-smart-scoping-rules) for social media sites, as well as the ability to crawl behind login walls using [browser profiles](https://docs.browsertrix.com/user-guide/browser-profiles/browser-profiles-overview/).",
+ caption:
+ "Webrecorder's social media presence, archived with Browsertrix.",
+ created: "2026-08-31T21:22:05Z",
+ modified: "2026-09-01T18:02:55Z",
+ lastStatsUpdateStarted: "2026-09-01T00:05:37Z",
+ crawlCount: 1,
+ pageCount: 8,
+ uniquePageCount: 8,
+ totalSize: 45471339,
+ dateEarliest: "2026-09-01T00:04:20.941000Z",
+ dateLatest: "2026-09-01T00:05:08.382000Z",
+ tags: [],
+ access: "unlisted",
+ homeUrl: "https://www.instagram.com/webrecorder/",
+ homeUrlTs: "2026-09-01T00:04:20.941000Z",
+ homeUrlPageId: "21ac772d-40a4-4594-89df-1d678053c170",
+ resources: [],
+ thumbnail: {
+ name: "fdc12a92-24c8-4c2d-aad1-9be73c4a5d31/images/-thumbnail-6eb71aa2-ef0b-4bcf-a03a-004b5a721c35-4zblpne3.jpeg",
+ path: "https://files.browsertrix.com/crawls/fdc12a92-24c8-4c2d-aad1-9be73c4a5d31/images/-thumbnail-6eb71aa2-ef0b-4bcf-a03a-004b5a721c35-4zblpne3.jpeg",
+ hash: "2be515acb3116de92a6f2833832c6b7d41120e81d5ec8e44525afac446b5ce04",
+ size: 28757,
+ mime: "image/jpeg",
+ originalFilename:
+ "page-thumbnail_21ac772d-40a4-4594-89df-1d678053c170.jpeg",
+ userid: "fake-9378-4cb5-93be-e3a4e74a39c2",
+ userName: "User Name",
+ created: "2026-09-01T16:15:21Z",
+ },
+ thumbnailSource: {
+ url: "https://www.instagram.com/webrecorder/",
+ urlTs: "2026-09-01T00:04:20.941000Z",
+ urlPageId: "21ac772d-40a4-4594-89df-1d678053c170",
+ },
+ defaultThumbnailName: null,
+ allowPublicDownload: true,
+ initialPages: [],
+ preloadResources: [],
+ pagesQueryUrl: "",
+ downloadUrl: null,
+ topPageHosts: [{ host: "www.instagram.com", count: 8 }],
+ indexLastSavedAt: "2026-09-01T00:10:13Z",
+ indexState: "idle",
+ indexStats: {
+ totalUrls: 984,
+ dupeUrls: 356,
+ conservedSize: 53110651,
+ totalCrawls: 1,
+ totalCrawlSize: 45471339,
+ removedCrawls: 0,
+ removedCrawlSize: 0,
+ updateProgress: 0.0,
+ uniqueHashes: 628,
+ estimatedRedundantSize: 0,
+ },
+ runningUpdatesCount: 0,
+ },
+ {
+ id: "fake-efb9-4613-bcc7-49dc4810fb01",
+ name: "Private",
+ slug: "private",
+ oid: "x_example_org_id_x",
+ description: null,
+ caption: "",
+ created: "2025-07-28T19:20:35Z",
+ modified: "2025-07-28T19:20:43Z",
+ lastStatsUpdateStarted: null,
+ crawlCount: 1,
+ pageCount: 1,
+ uniquePageCount: 1,
+ totalSize: 7744084,
+ dateEarliest: "2025-07-28T19:15:50.934000Z",
+ dateLatest: "2025-07-28T19:15:50.934000Z",
+ tags: [],
+ access: "private",
+ homeUrl: null,
+ homeUrlTs: null,
+ homeUrlPageId: null,
+ resources: [],
+ thumbnail: null,
+ thumbnailSource: null,
+ defaultThumbnailName: "thumbnail-cyan",
+ allowPublicDownload: true,
+ initialPages: [],
+ preloadResources: [],
+ pagesQueryUrl: "",
+ downloadUrl: null,
+ topPageHosts: [{ host: "webrecorder.net", count: 1 }],
+ indexLastSavedAt: null,
+ indexState: null,
+ indexStats: null,
+ runningUpdatesCount: 0,
+ },
+ ],
+};
diff --git a/frontend/src/components/layout/app-bar.ts b/frontend/src/components/layout/app-bar.ts
index 4b42df8331..cad9e81cad 100644
--- a/frontend/src/components/layout/app-bar.ts
+++ b/frontend/src/components/layout/app-bar.ts
@@ -16,6 +16,10 @@ import {
import { ORG_NAME_MAX_LENGTH } from "@/types/org";
import { type UserOrg } from "@/types/user";
import { type RouteName, type ViewState } from "@/utils/APIRouter";
+import {
+ completeOnboardingStep,
+ OnboardingStep,
+} from "@/utils/onboarding/onboardingEvents";
import { urlForName } from "@/utils/router";
import { AppStateService } from "@/utils/state";
import { tw } from "@/utils/tailwind";
@@ -267,7 +271,13 @@ export class AppBar extends BtrixElement {
${msg("Open to Side")}
-
+ {
+ completeOnboardingStep(OnboardingStep.OpenUserGuide);
+ }}
+ >
${msg("Open in New Tab")}
@@ -286,6 +296,8 @@ export class AppBar extends BtrixElement {
}
private readonly showUserGuide = () => {
+ completeOnboardingStep(OnboardingStep.OpenUserGuide);
+
this.dispatchEvent(
new CustomEvent(
"btrix-user-guide-show",
diff --git a/frontend/src/components/ui/index.ts b/frontend/src/components/ui/index.ts
index c8463fbb52..a639839904 100644
--- a/frontend/src/components/ui/index.ts
+++ b/frontend/src/components/ui/index.ts
@@ -8,6 +8,7 @@ import "./dialog";
import "./link";
import "./navigation";
import "./overflow-scroll";
+import "./popover";
import "./tab-group";
import "./tab-list";
import "./url-input";
@@ -36,7 +37,6 @@ import("./meter");
import("./numbered-list");
import("./overflow-dropdown");
import("./pagination");
-import("./popover");
import("./popover-menu");
import("./pw-strength-alert");
import("./prose");
diff --git a/frontend/src/constants/docs-email.ts b/frontend/src/constants/docs-email.ts
new file mode 100644
index 0000000000..5cd695625f
--- /dev/null
+++ b/frontend/src/constants/docs-email.ts
@@ -0,0 +1 @@
+export const docsEmail = "docs-feedback@webrecorder.net";
diff --git a/frontend/src/features/collections/collections-grid.ts b/frontend/src/features/collections/collections-grid.ts
index 6660c3e20b..5aa561c250 100644
--- a/frontend/src/features/collections/collections-grid.ts
+++ b/frontend/src/features/collections/collections-grid.ts
@@ -45,7 +45,7 @@ export class CollectionsGrid extends BtrixElement {
pagination!: Node[];
render() {
- const gridClassNames = tw`grid flex-1 grid-cols-1 gap-10 md:grid-cols-2 lg:grid-cols-3`;
+ const gridClassNames = tw`grid flex-1 grid-cols-1 gap-10 @md/org:grid-cols-2 @lg/org:grid-cols-3`;
if (!this.collections || !this.slug) {
const thumb = html`
@@ -56,7 +56,7 @@ export class CollectionsGrid extends BtrixElement {
`;
return html`
-
${thumb}${thumb}${thumb}${thumb}
+
${thumb}${thumb}${thumb}
`;
}
@@ -168,7 +168,11 @@ export class CollectionsGrid extends BtrixElement {
node.nodeName !== "SLOT").length &&
+ tw`mt-10`,
+ )}
name="pagination"
>
`;
diff --git a/frontend/src/features/crawl-workflows/workflow-editor.ts b/frontend/src/features/crawl-workflows/workflow-editor.ts
index c85cd44fbb..c59e55b265 100644
--- a/frontend/src/features/crawl-workflows/workflow-editor.ts
+++ b/frontend/src/features/crawl-workflows/workflow-editor.ts
@@ -134,6 +134,10 @@ import { makeCurrentTargetHandler, stopProp } from "@/utils/events";
import { formValidator, maxLengthValidator } from "@/utils/form";
import { isNotEqual } from "@/utils/is-not-equal";
import localize from "@/utils/localize";
+import {
+ completeOnboardingStep,
+ OnboardingStep,
+} from "@/utils/onboarding/onboardingEvents";
import { isArchivingDisabled } from "@/utils/orgs";
import { pluralOf } from "@/utils/pluralize";
import slugifyStrict from "@/utils/slugify";
@@ -3576,6 +3580,8 @@ https://archiveweb.page/images/${"logo.svg"}`}
id: "workflow-created-status",
});
+ completeOnboardingStep(OnboardingStep.CreateWorkflow);
+
this.navigate.to(
`${this.navigate.orgBasePath}/workflows/${this.configId || data.id}/${
crawlId && !storageQuotaReached && !executionMinutesQuotaReached
diff --git a/frontend/src/features/org/org-status-banner.ts b/frontend/src/features/org/org-status-banner.ts
index 5f5a74ed5a..7a4fa91472 100644
--- a/frontend/src/features/org/org-status-banner.ts
+++ b/frontend/src/features/org/org-status-banner.ts
@@ -53,7 +53,7 @@ const iconForVariant = (
};
// show banner as warning if <= this many days of trial is left
-const TRIAL_DAYS_LEFT_SHOW_WARNING = 4;
+export const TRIAL_DAYS_LEFT_SHOW_WARNING = 4;
@customElement("btrix-org-status-banner")
@localized()
diff --git a/frontend/src/index.ts b/frontend/src/index.ts
index c00c0d4fb1..5326cdefba 100644
--- a/frontend/src/index.ts
+++ b/frontend/src/index.ts
@@ -382,10 +382,18 @@ export class App extends BtrixElement {
AppStateService.updateUserGuideOpen(false)}
+ @sl-hide=${() => {
+ AppStateService.updateUserGuideOpen(false);
+ }}
+ @sl-after-hide=${() => {
+ if (this.userGuidePath.startsWith("?q=")) {
+ // Reset search
+ this.userGuidePath = "";
+ }
+ }}
>
diff --git a/frontend/src/layouts/page.ts b/frontend/src/layouts/page.ts
index d8a31344fa..90bf2d6393 100644
--- a/frontend/src/layouts/page.ts
+++ b/frontend/src/layouts/page.ts
@@ -10,15 +10,17 @@ import { tw } from "@/utils/tailwind";
export function pageHeading({
content,
level = 2,
+ classNames,
}: {
content?: string | TemplateResult | typeof nothing;
level?: 2 | 3 | 4;
+ classNames?: typeof tw | string;
}) {
const tag = unsafeStatic(`h${level}`);
- const classNames = tw`min-w-0 text-base font-medium leading-8 lg:text-lg`;
+ const classes = tw`min-w-0 text-base font-medium leading-8 lg:text-lg`;
return staticHtml`
- <${tag} class=${classNames}>
+ <${tag} class=${clsx(classes, classNames)}>
${
content ||
html``
diff --git a/frontend/src/pages/invite/accept.ts b/frontend/src/pages/invite/accept.ts
index b5ccb896b6..27e1953b3f 100644
--- a/frontend/src/pages/invite/accept.ts
+++ b/frontend/src/pages/invite/accept.ts
@@ -236,6 +236,10 @@ export class AcceptInvite extends BtrixElement {
} else {
const user = await this._getCurrentUser();
+ AppStateService.partialUpdateOnboarding({
+ orgId: org.id,
+ showOnboarding: true,
+ });
AppStateService.updateUser(formatAPIUser(user), org.slug);
await this.updateComplete;
diff --git a/frontend/src/pages/invite/join.ts b/frontend/src/pages/invite/join.ts
index de95841df7..512863b1e1 100644
--- a/frontend/src/pages/invite/join.ts
+++ b/frontend/src/pages/invite/join.ts
@@ -9,8 +9,10 @@ import { BtrixElement } from "@/classes/BtrixElement";
import type { SignUpSuccessDetail } from "@/features/accounts/sign-up-form";
import type { OrgUpdatedDetail } from "@/pages/invite/ui/org-form";
import { OrgTab, RouteNamespace } from "@/routes";
+import { type Onboarding } from "@/types/onboarding";
import type { UserInfo, UserOrg, UserOrgInviteInfo } from "@/types/user";
import AuthService, { type LoggedInEventDetail } from "@/utils/AuthService";
+import { AppStateService } from "@/utils/state";
import "./ui/org-form";
@@ -33,6 +35,21 @@ export class Join extends BtrixElement {
task: async ([token, email]) => {
if (!token || !email) throw new Error("Missing args");
const inviteInfo = await this._getInviteInfo({ token, email });
+
+ if (inviteInfo) {
+ const onboarding: Onboarding = isUserInfo(inviteInfo)
+ ? {
+ orgId: this.orgId || "",
+ showOnboarding: true,
+ }
+ : {
+ orgId: inviteInfo.oid,
+ showOnboarding: true,
+ };
+
+ AppStateService.partialUpdateOnboarding(onboarding);
+ }
+
return inviteInfo;
},
args: () => [this.token, this.email] as const,
diff --git a/frontend/src/pages/org/archived-item-detail/archived-item-detail.ts b/frontend/src/pages/org/archived-item-detail/archived-item-detail.ts
index e1b13c5c4d..61dee535fe 100644
--- a/frontend/src/pages/org/archived-item-detail/archived-item-detail.ts
+++ b/frontend/src/pages/org/archived-item-detail/archived-item-detail.ts
@@ -53,6 +53,10 @@ import {
renderName,
} from "@/utils/crawler";
import { humanizeExecutionSeconds } from "@/utils/executionTimeFormatter";
+import {
+ completeOnboardingStep,
+ OnboardingStep,
+} from "@/utils/onboarding/onboardingEvents";
import { isArchivingDisabled } from "@/utils/orgs";
import { pluralOf } from "@/utils/pluralize";
import { richText } from "@/utils/rich-text";
@@ -968,6 +972,11 @@ export class ArchivedItemDetail extends BtrixElement {
replayBase="/replay/"
noSandbox="true"
noCache="true"
+ @rwp-url-change=${() => {
+ if (this.itemType === "crawl") {
+ completeOnboardingStep(OnboardingStep.ReplayItem);
+ }
+ }}
>
`
: html`
diff --git a/frontend/src/pages/org/dashboard.ts b/frontend/src/pages/org/dashboard.ts
index 36159a7b0d..31a45fee6c 100644
--- a/frontend/src/pages/org/dashboard.ts
+++ b/frontend/src/pages/org/dashboard.ts
@@ -1,4 +1,4 @@
-import { localized, msg } from "@lit/localize";
+import { localized, msg, str } from "@lit/localize";
import { Task, TaskStatus } from "@lit/task";
import type {
SlChangeEvent,
@@ -7,43 +7,62 @@ import type {
} from "@shoelace-style/shoelace";
import clsx from "clsx";
import { html, nothing, type PropertyValues, type TemplateResult } from "lit";
-import { customElement, property, state } from "lit/decorators.js";
+import { customElement, property, query, state } from "lit/decorators.js";
+import { guard } from "lit/directives/guard.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { when } from "lit/directives/when.js";
import queryString from "query-string";
+import { dashboardHeading } from "./dashboard/layouts/dashboardHeading";
+import { primaryWithAside } from "./dashboard/layouts/primaryWithAside";
+import { docsFeedback } from "./dashboard/templates/docsFeedback";
+import { generalGuides } from "./dashboard/templates/generalGuides";
+import { onboardingChecklist } from "./dashboard/templates/onboardingChecklist";
+import { resourcesList } from "./dashboard/templates/resourcesList";
+
import type { SelectNewDialogEvent } from ".";
import { BtrixElement } from "@/classes/BtrixElement";
+import { type Dialog } from "@/components/ui/dialog";
import { parsePage, type PageChangeEvent } from "@/components/ui/pagination";
+import { docsEmail } from "@/constants/docs-email";
+import { type BtrixUserGuideShowEvent } from "@/events/btrix-user-guide-show";
import { storageColorClasses } from "@/features/meters/storage/colors";
+import {
+ OrgStatusBanner,
+ TRIAL_DAYS_LEFT_SHOW_WARNING,
+} from "@/features/org/org-status-banner";
import { pageHeading } from "@/layouts/page";
import { pageHeader } from "@/layouts/pageHeader";
import type { CollectionSavedEvent } from "@/pages/org/collection-detail/types";
-import { RouteNamespace } from "@/routes";
+import { type Tab as SettingsTab } from "@/pages/org/settings/settings";
+import { pluralOfTrialDaysRemaining } from "@/plurals/trial-days-remaining";
+import { OrgTab, RouteNamespace } from "@/routes";
+import { AnalyticsTrackEvent } from "@/trackEvents";
import type { APIPaginatedList, APISortQuery } from "@/types/api";
import { CollectionAccess, type Collection } from "@/types/collection";
import { type Metrics } from "@/types/org";
import { SortDirection } from "@/types/utils";
-import { richText } from "@/utils/rich-text";
+import { track } from "@/utils/analytics";
+import { onboardingSteps } from "@/utils/onboarding/onboardingEvents";
+import { hasUsage } from "@/utils/orgs";
+import { AppStateService } from "@/utils/state";
import { tw } from "@/utils/tailwind";
import { timeoutCache } from "@/utils/timeoutCache";
-import { toShortUrl } from "@/utils/url-helpers";
import { cached } from "@/utils/weakCache";
+import "@/pages/org/dashboard/components/dashboard-guides";
+
enum CollectionGridView {
All = "all",
Public = "public",
}
-const PAGE_SIZE = 16;
+const PAGE_SIZE = 12;
@customElement("btrix-dashboard")
@localized()
export class Dashboard extends BtrixElement {
- @property({ type: Boolean })
- isCrawler?: boolean;
-
@state()
private metrics?: Metrics;
@@ -56,6 +75,12 @@ export class Dashboard extends BtrixElement {
@state()
collectionPage = parsePage(new URLSearchParams(location.search).get("page"));
+ @property({ type: Boolean })
+ showOnboardingFinishedDialog = false;
+
+ @query("#onboarding-finished")
+ private readonly onboardingFinishedDialog?: Dialog | null;
+
// Used for busting cache when updating visible collection
cacheBust = 0;
@@ -106,64 +131,15 @@ export class Dashboard extends BtrixElement {
return html`
${pageHeader({
- title: this.userOrg?.name,
- secondary: html`
- ${when(
- this.org?.publicDescription,
- (publicDescription) => html`
-
+
+ track(AnalyticsTrackEvent.OpenedCrawlingOnePageGuide, trackProps)}
+ >
+ ${msg("One Page")}
+ ${msg("Archive a single page on a website")}
+
+
+ track(
+ AnalyticsTrackEvent.OpenedCrawlingSocialMediaGuide,
+ trackProps,
+ )}
+ >
+ ${msg("Social Media Page")}
+ ${msg("Archive a social media post or profile")}
+
+
+ track(AnalyticsTrackEvent.OpenedCrawlingWebsiteGuide, trackProps)}
+ >
+ ${msg("Entire Website")}
+ ${msg("Archive every page on a website")}
+
+
+ ${this.renderGuideLink({
+ label: msg("Change how your name appears to team members"),
+ icon: "file-person-fill",
+ path: "user-settings/#display-name",
+ })}
+