Archive and crawl navigation improvements (#154)
This commit is contained in:
parent
b5874c3f8c
commit
4af30a02be
@ -44,11 +44,7 @@ export class App extends LiteElement {
|
||||
userInfo?: CurrentUser;
|
||||
|
||||
@state()
|
||||
private viewState!: ViewState & {
|
||||
aid?: string;
|
||||
// TODO common tab type
|
||||
tab?: "running" | "finished" | "configs";
|
||||
};
|
||||
private viewState!: ViewState;
|
||||
|
||||
@state()
|
||||
private globalDialogContent: DialogContent = {};
|
||||
@ -203,9 +199,9 @@ export class App extends LiteElement {
|
||||
|
||||
renderNavBar() {
|
||||
return html`
|
||||
<div class="bg-gray-900 text-gray-50">
|
||||
<div class="border-b">
|
||||
<nav
|
||||
class="max-w-screen-lg mx-auto p-2 box-border flex items-center justify-between"
|
||||
class="max-w-screen-lg mx-auto px-3 box-border h-12 flex items-center justify-between"
|
||||
>
|
||||
<div>
|
||||
<a href="/archives" @click="${this.navLink}"
|
||||
@ -214,7 +210,7 @@ export class App extends LiteElement {
|
||||
</div>
|
||||
<div class="grid grid-flow-col gap-5 items-center">
|
||||
${this.authService.authState
|
||||
? html` <sl-dropdown>
|
||||
? html` <sl-dropdown placement="bottom-end">
|
||||
<div class="p-2" role="button" slot="trigger">
|
||||
${this.userInfo?.name || this.userInfo?.email}
|
||||
<span class="text-xs"
|
||||
@ -241,12 +237,16 @@ export class App extends LiteElement {
|
||||
</sl-dropdown>`
|
||||
: html`
|
||||
<a href="/log-in"> ${msg("Log In")} </a>
|
||||
<sl-button
|
||||
outline
|
||||
@click="${() => this.navigate("/sign-up")}"
|
||||
>
|
||||
<span class="text-white">${msg("Sign up")}</span>
|
||||
</sl-button>
|
||||
${this.isRegistrationEnabled
|
||||
? html`
|
||||
<sl-button
|
||||
type="text"
|
||||
@click="${() => this.navigate("/sign-up")}"
|
||||
>
|
||||
${msg("Sign up")}
|
||||
</sl-button>
|
||||
`
|
||||
: html``}
|
||||
`}
|
||||
</div>
|
||||
</nav>
|
||||
@ -255,32 +255,6 @@ export class App extends LiteElement {
|
||||
}
|
||||
|
||||
renderPage() {
|
||||
const navLink = ({
|
||||
activeRoutes,
|
||||
href,
|
||||
label,
|
||||
}: {
|
||||
activeRoutes: string[];
|
||||
href: string;
|
||||
label: string;
|
||||
}) => html`
|
||||
<li>
|
||||
<a
|
||||
class="block p-2 ${activeRoutes.includes(this.viewState.route!)
|
||||
? "text-primary"
|
||||
: ""}"
|
||||
href="${href}"
|
||||
@click="${this.navLink}"
|
||||
>${label}</a
|
||||
>
|
||||
</li>
|
||||
`;
|
||||
const appLayout = (template: TemplateResult) => html`
|
||||
<div class="w-full max-w-screen-lg mx-auto p-2 md:py-8 box-border">
|
||||
${template}
|
||||
</div>
|
||||
`;
|
||||
|
||||
switch (this.viewState.route) {
|
||||
case "signUp": {
|
||||
if (!this.isAppSettingsLoaded) {
|
||||
@ -365,13 +339,13 @@ export class App extends LiteElement {
|
||||
</div>`;
|
||||
|
||||
case "archives":
|
||||
return appLayout(html`<btrix-archives
|
||||
class="w-full"
|
||||
return html`<btrix-archives
|
||||
class="w-full max-w-screen-lg mx-auto p-2 md:py-8 box-border"
|
||||
@navigate="${this.onNavigateTo}"
|
||||
@need-login="${this.onNeedLogin}"
|
||||
.authState="${this.authService.authState}"
|
||||
.userInfo="${this.userInfo}"
|
||||
></btrix-archives>`);
|
||||
></btrix-archives>`;
|
||||
|
||||
case "archive":
|
||||
case "archiveAddMember":
|
||||
@ -379,7 +353,7 @@ export class App extends LiteElement {
|
||||
case "crawl":
|
||||
case "crawlTemplate":
|
||||
case "crawlTemplateEdit":
|
||||
return appLayout(html`<btrix-archive
|
||||
return html`<btrix-archive
|
||||
class="w-full"
|
||||
@navigate=${this.onNavigateTo}
|
||||
@need-login=${this.onNeedLogin}
|
||||
@ -394,37 +368,26 @@ export class App extends LiteElement {
|
||||
?isAddingMember=${this.viewState.route === "archiveAddMember"}
|
||||
?isNewResourceTab=${this.viewState.route === "archiveNewResourceTab"}
|
||||
?isEditing=${Boolean(this.viewState.params.edit)}
|
||||
></btrix-archive>`);
|
||||
></btrix-archive>`;
|
||||
|
||||
case "accountSettings":
|
||||
return appLayout(html`<btrix-account-settings
|
||||
class="w-full"
|
||||
return html`<btrix-account-settings
|
||||
class="w-full max-w-screen-lg mx-auto p-2 md:py-8 box-border"
|
||||
@navigate="${this.onNavigateTo}"
|
||||
@need-login="${this.onNeedLogin}"
|
||||
.authState="${this.authService.authState}"
|
||||
.userInfo="${this.userInfo}"
|
||||
></btrix-account-settings>`);
|
||||
|
||||
case "archive-info":
|
||||
case "archive-info-tab":
|
||||
return appLayout(html`<btrix-archive
|
||||
class="w-full"
|
||||
@navigate="${this.onNavigateTo}"
|
||||
.authState="${this.authService.authState}"
|
||||
.viewState="${this.viewState}"
|
||||
aid="${this.viewState.params.aid}"
|
||||
tab="${this.viewState.tab || "running"}"
|
||||
></btrix-archive>`);
|
||||
></btrix-account-settings>`;
|
||||
|
||||
case "usersInvite": {
|
||||
if (this.userInfo?.isAdmin) {
|
||||
return appLayout(html`<btrix-users-invite
|
||||
class="w-full"
|
||||
return html`<btrix-users-invite
|
||||
class="w-full max-w-screen-lg mx-auto p-2 md:py-8 box-border"
|
||||
@navigate="${this.onNavigateTo}"
|
||||
@need-login="${this.onNeedLogin}"
|
||||
.authState="${this.authService.authState}"
|
||||
.userInfo="${this.userInfo}"
|
||||
></btrix-users-invite>`);
|
||||
></btrix-users-invite>`;
|
||||
} else {
|
||||
return this.renderNotFoundPage();
|
||||
}
|
||||
@ -554,20 +517,24 @@ export class App extends LiteElement {
|
||||
noHeader: true,
|
||||
body: html`
|
||||
<div class="grid gap-4 text-center">
|
||||
<p class="mt-8 text-2xl font-medium">Welcome to Browsertrix Cloud!</p>
|
||||
<p class="mt-8 text-2xl font-medium">
|
||||
${msg("Welcome to Browsertrix Cloud!")}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A confirmation email was sent to: <br />
|
||||
<strong>${email}</strong>.
|
||||
${msg(html`A confirmation email was sent to: <br />
|
||||
<strong>${email}</strong>.`)}
|
||||
</p>
|
||||
<p class="max-w-xs mx-auto">
|
||||
Click the link in your email to confirm your email address.
|
||||
${msg(
|
||||
"Click the link in your email to confirm your email address."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-4 mt-8 text-center">
|
||||
<sl-button type="primary" @click=${() => this.closeDialog()}
|
||||
>Got it, go to dashboard</sl-button
|
||||
>${msg("Got it, go to dashboard")}</sl-button
|
||||
>
|
||||
</div>
|
||||
`,
|
||||
|
@ -1,3 +1,4 @@
|
||||
import type { TemplateResult } from "lit";
|
||||
import { state, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import { msg, localized, str } from "@lit/localize";
|
||||
@ -7,6 +8,8 @@ import type { AuthState } from "../../utils/AuthService";
|
||||
import LiteElement, { html } from "../../utils/LiteElement";
|
||||
import type { Crawl } from "./types";
|
||||
|
||||
type SectionName = "overview" | "watch" | "download" | "logs";
|
||||
|
||||
const POLL_INTERVAL_SECONDS = 10;
|
||||
|
||||
/**
|
||||
@ -35,6 +38,9 @@ export class CrawlDetail extends LiteElement {
|
||||
@state()
|
||||
private isWatchExpanded: boolean = false;
|
||||
|
||||
@state()
|
||||
private sectionName: SectionName = "overview";
|
||||
|
||||
// For long polling:
|
||||
private timerId?: number;
|
||||
|
||||
@ -52,92 +58,225 @@ export class CrawlDetail extends LiteElement {
|
||||
// }
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
// Set initial active section based on URL #hash value
|
||||
const hash = window.location.hash.slice(1);
|
||||
if (["overview", "watch", "download", "logs"].includes(hash)) {
|
||||
this.sectionName = hash as SectionName;
|
||||
}
|
||||
super.connectedCallback();
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
this.stopPollTimer();
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
render() {
|
||||
let sectionContent: string | TemplateResult = "";
|
||||
|
||||
switch (this.sectionName) {
|
||||
case "watch":
|
||||
sectionContent = this.renderWatch();
|
||||
break;
|
||||
case "download":
|
||||
sectionContent = this.renderFiles();
|
||||
break;
|
||||
case "logs":
|
||||
sectionContent = this.renderLogs();
|
||||
break;
|
||||
default:
|
||||
sectionContent = this.renderOverview();
|
||||
break;
|
||||
}
|
||||
|
||||
return html`
|
||||
<nav class="mb-5">
|
||||
<a
|
||||
class="text-gray-600 hover:text-gray-800 text-sm font-medium"
|
||||
href=${`/archives/${this.archiveId}/crawls`}
|
||||
@click=${this.navLink}
|
||||
>
|
||||
<sl-icon
|
||||
name="arrow-left"
|
||||
class="inline-block align-middle"
|
||||
></sl-icon>
|
||||
<span class="inline-block align-middle"
|
||||
>${msg("Back to Crawls")}</span
|
||||
<div class="grid grid-cols-6 gap-4 items-start pb-3 mb-2 border-b">
|
||||
<div class="col-span-6 md:col-span-1">
|
||||
<a
|
||||
class="text-neutral-500 hover:text-neutral-600 text-sm font-medium"
|
||||
href=${`/archives/${this.archiveId}/crawls`}
|
||||
@click=${this.navLink}
|
||||
>
|
||||
</a>
|
||||
<sl-icon
|
||||
name="arrow-left"
|
||||
class="inline-block align-middle"
|
||||
></sl-icon>
|
||||
<span class="inline-block align-middle"
|
||||
>${msg("Back to Crawls")}</span
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-span-6 md:col-span-5">${this.renderHeader()}</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-6 gap-4">
|
||||
<div class="col-span-6 md:col-span-1">${this.renderNav()}</div>
|
||||
<div class="col-span-6 md:col-span-5 md:py-2">
|
||||
<main>${sectionContent}</main>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderNav() {
|
||||
const renderNavItem = ({
|
||||
section,
|
||||
label,
|
||||
}: {
|
||||
section: SectionName;
|
||||
label: any;
|
||||
}) => {
|
||||
const isActive = section === this.sectionName;
|
||||
return html`
|
||||
<li
|
||||
class="relative"
|
||||
role="menuitem"
|
||||
aria-selected=${isActive ? "true" : "false"}
|
||||
>
|
||||
<div
|
||||
class="absolute left-0 top-2 h-6 border-l-2 rounded-full transition-colors ${section ===
|
||||
this.sectionName
|
||||
? "border-l-primary"
|
||||
: "border-l-transparent"}"
|
||||
role="presentation"
|
||||
></div>
|
||||
<a
|
||||
class="block ml-2 p-2 font-medium rounded hover:bg-neutral-50 ${isActive
|
||||
? "text-primary"
|
||||
: "text-neutral-500 hover:text-neutral-900"}"
|
||||
href=${`/archives/${this.archiveId}/crawls/crawl/${this.crawlId}#${section}`}
|
||||
@click=${() => (this.sectionName = section)}
|
||||
>
|
||||
${label}
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
};
|
||||
return html`
|
||||
<nav class="border-b md:border-b-0">
|
||||
<ul class="flex flex-row md:flex-col" role="menu">
|
||||
${renderNavItem({ section: "overview", label: msg("Overview") })}
|
||||
${renderNavItem({ section: "watch", label: msg("View Crawl") })}
|
||||
${renderNavItem({ section: "download", label: msg("Download") })}
|
||||
${renderNavItem({ section: "logs", label: msg("Logs") })}
|
||||
</ul>
|
||||
</nav>
|
||||
`;
|
||||
}
|
||||
|
||||
<header class="my-3">
|
||||
<h2 class="text-xl font-medium mb-1 md:h-7">
|
||||
${this.crawl
|
||||
? msg(str`Crawl of ${this.crawl.configName}`)
|
||||
: html`<sl-skeleton style="width: 37em"></sl-skeleton>`}
|
||||
</h2>
|
||||
</header>
|
||||
private renderHeader() {
|
||||
const isRunning = this.crawl?.state === "running";
|
||||
|
||||
<section class="px-4 py-3 border-t border-b mb-4 text-sm">
|
||||
<dl class="grid grid-cols-2">
|
||||
return html`
|
||||
<header>
|
||||
<div class="flex justify-between">
|
||||
<h2 class="text-lg font-medium mb-3 md:h-6">
|
||||
${msg(
|
||||
html`<span class="font-normal">Crawl of</span> ${this.crawl
|
||||
? this.crawl.configName
|
||||
: html`<sl-skeleton
|
||||
class="inline-block"
|
||||
style="width: 15em"
|
||||
></sl-skeleton>`}`
|
||||
)}
|
||||
</h2>
|
||||
<div>
|
||||
<dt class="text-xs text-0-600">${msg("Crawl ID")}</dt>
|
||||
<dd class="h-5 whitespace-nowrap truncate font-mono text-xs">
|
||||
${this.crawl?.id ||
|
||||
html`<sl-skeleton style="width: 37em"></sl-skeleton>`}
|
||||
</dd>
|
||||
${isRunning
|
||||
? html`
|
||||
<sl-button class="mr-2" size="small" @click=${this.stop}>
|
||||
${msg("Stop Crawl")}
|
||||
</sl-button>
|
||||
<sl-button size="small" type="danger" @click=${this.cancel}>
|
||||
${msg("Cancel Crawl")}
|
||||
</sl-button>
|
||||
`
|
||||
: this.crawl
|
||||
? html`
|
||||
<sl-button
|
||||
href=${`/archives/${this.archiveId}/crawl-templates/config/${this.crawl.cid}`}
|
||||
size="small"
|
||||
@click=${this.navLink}
|
||||
>
|
||||
${msg("View Config")}
|
||||
</sl-button>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-xs text-0-600">${msg("Crawl Template")}</dt>
|
||||
<dd class="h-5 whitespace-nowrap truncate">
|
||||
</div>
|
||||
<dl class="grid grid-cols-4 gap-5">
|
||||
<div class="col-span-1">
|
||||
<dt class="text-sm text-0-600">${msg("Status")}</dt>
|
||||
<dd>
|
||||
${this.crawl
|
||||
? html`
|
||||
<a
|
||||
class="text-primary font-medium hover:underline"
|
||||
href=${`/archives/${this.archiveId}/crawl-templates/config/${this.crawl.cid}`}
|
||||
@click=${this.navLink}
|
||||
>${this.crawl.configName}</a
|
||||
>
|
||||
<div class="flex items-baseline justify-between">
|
||||
<div
|
||||
class="whitespace-nowrap capitalize${isRunning
|
||||
? " motion-safe:animate-pulse"
|
||||
: ""}"
|
||||
>
|
||||
<span
|
||||
class="inline-block ${this.crawl.state === "failed"
|
||||
? "text-red-500"
|
||||
: this.crawl.state === "complete"
|
||||
? "text-emerald-500"
|
||||
: isRunning
|
||||
? "text-purple-500"
|
||||
: "text-zinc-300"}"
|
||||
style="font-size: 10px; vertical-align: 2px"
|
||||
>
|
||||
●
|
||||
</span>
|
||||
${this.crawl.state.replace(/_/g, " ")}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: html`<sl-skeleton style="width: 15em"></sl-skeleton>`}
|
||||
: html`<sl-skeleton class="h-6"></sl-skeleton>`}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<dt class="text-sm text-0-600">${msg("Pages Crawled")}</dt>
|
||||
<dd>
|
||||
${this.crawl?.stats
|
||||
? html`
|
||||
<span
|
||||
class="font-mono tracking-tighter${isRunning
|
||||
? " text-purple-600"
|
||||
: ""}"
|
||||
>
|
||||
${this.numberFormatter.format(+this.crawl.stats.done)}
|
||||
<span class="text-0-400">/</span>
|
||||
${this.numberFormatter.format(+this.crawl.stats.found)}
|
||||
</span>
|
||||
`
|
||||
: html`<sl-skeleton class="h-6"></sl-skeleton>`}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<dt class="text-sm text-0-600">${msg("Run Duration")}</dt>
|
||||
<dd>
|
||||
${this.crawl
|
||||
? html`
|
||||
${this.crawl.finished
|
||||
? html`${RelativeDuration.humanize(
|
||||
new Date(`${this.crawl.finished}Z`).valueOf() -
|
||||
new Date(`${this.crawl.started}Z`).valueOf()
|
||||
)}`
|
||||
: html`
|
||||
<span class="text-purple-600">
|
||||
<btrix-relative-duration
|
||||
value=${`${this.crawl.started}Z`}
|
||||
></btrix-relative-duration>
|
||||
</span>
|
||||
`}
|
||||
`
|
||||
: html`<sl-skeleton class="h-6"></sl-skeleton>`}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<!-- TODO created at? -->
|
||||
</section>
|
||||
|
||||
<main class="grid gap-5">
|
||||
<section
|
||||
class="grid grid-cols-2 md:grid-cols-8 gap-3 rounded-lg md:p-4 md:bg-zinc-100"
|
||||
>
|
||||
<div
|
||||
class="col-span-8 ${this.isWatchExpanded
|
||||
? "md:col-span-8"
|
||||
: "md:col-span-5"} relative"
|
||||
>
|
||||
${this.renderWatch()}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="col-span-8 ${this.isWatchExpanded
|
||||
? "md:col-span-8"
|
||||
: "md:col-span-3"} border rounded-lg bg-white p-4 md:p-8"
|
||||
>
|
||||
${this.renderDetails()}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 class="text-lg font-medium mb-2">${msg("Download Files")}</h3>
|
||||
${this.renderFiles()}
|
||||
</section>
|
||||
</main>
|
||||
</header>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -151,6 +290,8 @@ export class CrawlDetail extends LiteElement {
|
||||
const replaySource = this.crawl?.resources?.[0]?.path;
|
||||
|
||||
return html`
|
||||
<h3 class="text-lg font-medium mb-2">${msg("Watch or Replay Crawl")}</h3>
|
||||
|
||||
<div
|
||||
class="aspect-video rounded border ${isRunning
|
||||
? "border-purple-200"
|
||||
@ -201,106 +342,10 @@ export class CrawlDetail extends LiteElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private renderDetails() {
|
||||
const isRunning = this.crawl?.state === "running";
|
||||
|
||||
private renderOverview() {
|
||||
return html`
|
||||
<dl class="grid grid-cols-2 gap-5">
|
||||
<div class="col-span-2">
|
||||
<dt class="text-sm text-0-600">${msg("Status")}</dt>
|
||||
<dd>
|
||||
${this.crawl
|
||||
? html`
|
||||
<div class="flex items-baseline justify-between">
|
||||
<div
|
||||
class="whitespace-nowrap capitalize${isRunning
|
||||
? " motion-safe:animate-pulse"
|
||||
: ""}"
|
||||
>
|
||||
<span
|
||||
class="inline-block ${this.crawl.state === "failed"
|
||||
? "text-red-500"
|
||||
: this.crawl.state === "complete"
|
||||
? "text-emerald-500"
|
||||
: isRunning
|
||||
? "text-purple-500"
|
||||
: "text-zinc-300"}"
|
||||
style="font-size: 10px; vertical-align: 2px"
|
||||
>
|
||||
●
|
||||
</span>
|
||||
${this.crawl.state.replace(/_/g, " ")}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: html`<sl-skeleton class="h-6"></sl-skeleton>`}
|
||||
${isRunning
|
||||
? html`
|
||||
<sl-details
|
||||
class="mt-2"
|
||||
style="--sl-spacing-medium: var(--sl-spacing-x-small)"
|
||||
>
|
||||
<span slot="summary" class="text-sm text-0-700">
|
||||
${msg("Manage")}
|
||||
</span>
|
||||
|
||||
<div class="mb-3 text-center text-sm leading-none">
|
||||
<sl-button class="mr-2" size="small" @click=${this.stop}>
|
||||
${msg("Stop Crawl")}
|
||||
</sl-button>
|
||||
<sl-button
|
||||
size="small"
|
||||
type="danger"
|
||||
@click=${this.cancel}
|
||||
>
|
||||
${msg("Cancel Crawl")}
|
||||
</sl-button>
|
||||
</div>
|
||||
</sl-details>
|
||||
`
|
||||
: ""}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<dt class="text-sm text-0-600">${msg("Pages Crawled")}</dt>
|
||||
<dd>
|
||||
${this.crawl?.stats
|
||||
? html`
|
||||
<span
|
||||
class="font-mono tracking-tighter${isRunning
|
||||
? " text-purple-600"
|
||||
: ""}"
|
||||
>
|
||||
${this.numberFormatter.format(+this.crawl.stats.done)}
|
||||
<span class="text-0-400">/</span>
|
||||
${this.numberFormatter.format(+this.crawl.stats.found)}
|
||||
</span>
|
||||
`
|
||||
: html`<sl-skeleton class="h-6"></sl-skeleton>`}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<dt class="text-sm text-0-600">${msg("Run Duration")}</dt>
|
||||
<dd>
|
||||
${this.crawl
|
||||
? html`
|
||||
${this.crawl.finished
|
||||
? html`${RelativeDuration.humanize(
|
||||
new Date(`${this.crawl.finished}Z`).valueOf() -
|
||||
new Date(`${this.crawl.started}Z`).valueOf()
|
||||
)}`
|
||||
: html`
|
||||
<span class="text-purple-600">
|
||||
<btrix-relative-duration
|
||||
value=${`${this.crawl.started}Z`}
|
||||
></btrix-relative-duration>
|
||||
</span>
|
||||
`}
|
||||
`
|
||||
: html`<sl-skeleton class="h-6"></sl-skeleton>`}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<dt class="text-sm text-0-600">${msg("Started")}</dt>
|
||||
<dd>
|
||||
${this.crawl
|
||||
@ -318,7 +363,7 @@ export class CrawlDetail extends LiteElement {
|
||||
: html`<sl-skeleton class="h-6"></sl-skeleton>`}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<div class="col-span-1">
|
||||
<dt class="text-sm text-0-600">${msg("Finished")}</dt>
|
||||
<dd>
|
||||
${this.crawl
|
||||
@ -338,7 +383,7 @@ export class CrawlDetail extends LiteElement {
|
||||
: html`<sl-skeleton class="h-6"></sl-skeleton>`}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<div class="col-span-1">
|
||||
<dt class="text-sm text-0-600">${msg("Reason")}</dt>
|
||||
<dd>
|
||||
${this.crawl
|
||||
@ -355,12 +400,48 @@ export class CrawlDetail extends LiteElement {
|
||||
: html`<sl-skeleton class="h-6"></sl-skeleton>`}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<dt class="text-sm text-0-600">${msg("Crawl Template")}</dt>
|
||||
<dd>
|
||||
${this.crawl
|
||||
? html`
|
||||
<a
|
||||
class="font-medium text-neutral-700 hover:text-neutral-900"
|
||||
href=${`/archives/${this.archiveId}/crawl-templates/config/${this.crawl.cid}`}
|
||||
@click=${this.navLink}
|
||||
>
|
||||
<sl-icon
|
||||
class="inline-block align-middle"
|
||||
name="link-45deg"
|
||||
></sl-icon>
|
||||
<span class="inline-block align-middle">
|
||||
${this.crawl.configName}
|
||||
</span>
|
||||
</a>
|
||||
`
|
||||
: html`<sl-skeleton class="h-6"></sl-skeleton>`}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<dt class="text-sm text-0-600">${msg("Crawl ID")}</dt>
|
||||
<dd class="truncate">
|
||||
${this.crawl
|
||||
? html`<btrix-copy-button
|
||||
value=${this.crawl.id}
|
||||
></btrix-copy-button>
|
||||
<code class="text-xs" title=${this.crawl.id}
|
||||
>${this.crawl.id}</code
|
||||
> `
|
||||
: html`<sl-skeleton class="h-6"></sl-skeleton>`}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderFiles() {
|
||||
return html`
|
||||
<h3 class="text-lg font-medium mb-2">${msg("Download Files")}</h3>
|
||||
<ul class="border rounded text-sm">
|
||||
${this.crawl?.resources?.map(
|
||||
(file) => html`
|
||||
@ -382,6 +463,10 @@ export class CrawlDetail extends LiteElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private renderLogs() {
|
||||
return html`TODO`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch crawl and update internal state
|
||||
*/
|
||||
|
@ -118,7 +118,7 @@ export class CrawlTemplatesDetail extends LiteElement {
|
||||
<sl-button
|
||||
size="small"
|
||||
type="text"
|
||||
@click=${() => (this.openDialogName = "config")}
|
||||
@click=${() => (this.openDialogName = "name")}
|
||||
>
|
||||
${msg("Edit")}
|
||||
</sl-button>
|
||||
|
@ -80,11 +80,14 @@ export class Archive extends LiteElement {
|
||||
|
||||
render() {
|
||||
if (!this.archive) {
|
||||
return html`<div
|
||||
class="w-full flex items-center justify-center my-24 text-4xl"
|
||||
>
|
||||
<sl-spinner></sl-spinner>
|
||||
</div>`;
|
||||
return html`
|
||||
<div
|
||||
class="absolute top-1/2 left-1/2 -mt-4 -ml-4"
|
||||
style="font-size: 2rem"
|
||||
>
|
||||
<sl-spinner></sl-spinner>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const showMembersTab = Boolean(this.archive.users);
|
||||
@ -112,20 +115,22 @@ export class Archive extends LiteElement {
|
||||
break;
|
||||
}
|
||||
|
||||
return html`<article class="grid gap-4">
|
||||
<nav class="font-medium text-sm text-gray-500">
|
||||
<a
|
||||
class="text-primary hover:underline"
|
||||
href="/archives"
|
||||
@click="${this.navLink}"
|
||||
>${msg("Archives")}</a
|
||||
>
|
||||
<span class="font-mono">/</span>
|
||||
<span>${this.archive.name}</span>
|
||||
</nav>
|
||||
return html`<article>
|
||||
<header class="w-full max-w-screen-lg mx-auto px-3 box-border py-4">
|
||||
<nav class="text-sm text-neutral-400">
|
||||
<a
|
||||
class="font-medium hover:underline"
|
||||
href="/archives"
|
||||
@click="${this.navLink}"
|
||||
>${msg("Archives")}</a
|
||||
>
|
||||
<span class="font-mono">/</span>
|
||||
<span>${this.archive.name}</span>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<nav class="flex items-end overflow-x-auto">
|
||||
<div class="w-full max-w-screen-lg mx-auto px-3 box-border">
|
||||
<nav class="-ml-3 flex items-end overflow-x-auto">
|
||||
${this.renderNavTab({ tabName: "crawls", label: msg("Crawls") })}
|
||||
${this.renderNavTab({
|
||||
tabName: "crawl-templates",
|
||||
@ -134,10 +139,16 @@ export class Archive extends LiteElement {
|
||||
${showMembersTab
|
||||
? this.renderNavTab({ tabName: "members", label: msg("Members") })
|
||||
: ""}
|
||||
<hr class="flex-1 border-t-2" />
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="my-4" aria-labelledby="${this.archiveTab}-tab">
|
||||
<hr />
|
||||
|
||||
<main>
|
||||
<div
|
||||
class="w-full max-w-screen-lg mx-auto px-3 box-border py-5"
|
||||
aria-labelledby="${this.archiveTab}-tab"
|
||||
>
|
||||
${tabPanelContent}
|
||||
</div>
|
||||
</main>
|
||||
@ -156,14 +167,18 @@ export class Archive extends LiteElement {
|
||||
return html`
|
||||
<a
|
||||
id="${tabName}-tab"
|
||||
class="block flex-shrink-0 text-sm font-medium p-3 md:px-5 border-b-2 transition-colors ${isActive
|
||||
? "border-primary text-primary"
|
||||
: "text-0-600"}"
|
||||
class="block flex-shrink-0 px-3 hover:bg-neutral-50 rounded-t transition-colors"
|
||||
href=${`/archives/${this.archiveId}/${tabName}`}
|
||||
aria-selected=${isActive}
|
||||
@click=${this.navLink}
|
||||
>
|
||||
${label}
|
||||
<div
|
||||
class="text-sm font-medium py-3 border-b-2 transition-colors ${isActive
|
||||
? "border-primary text-primary"
|
||||
: "border-transparent text-neutral-500 hover:border-neutral-100 hover:text-neutral-900"}"
|
||||
>
|
||||
${label}
|
||||
</div>
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user