|
| 1 | +import { localized, msg } from "@lit/localize"; |
| 2 | +import { Task } from "@lit/task"; |
| 3 | +import type { SlTree } from "@shoelace-style/shoelace"; |
| 4 | +import { html, nothing, unsafeCSS } from "lit"; |
| 5 | +import { customElement, property, query } from "lit/decorators.js"; |
| 6 | +import { repeat } from "lit/directives/repeat.js"; |
| 7 | +import queryString from "query-string"; |
| 8 | + |
| 9 | +import { collectionStatusIcon } from "../templates/collection-status-icon"; |
| 10 | + |
| 11 | +import stylesheet from "./item-dependency-list.stylesheet.css"; |
| 12 | + |
| 13 | +import { BtrixElement } from "@/classes/BtrixElement"; |
| 14 | +import { dedupeIcon } from "@/features/collections/templates/dedupe-icon"; |
| 15 | +import type { ArchivedItemSectionName } from "@/pages/org/archived-item-detail/archived-item-detail"; |
| 16 | +import { OrgTab, WorkflowTab } from "@/routes"; |
| 17 | +import type { APIPaginatedList } from "@/types/api"; |
| 18 | +import type { ArchivedItem } from "@/types/crawler"; |
| 19 | +import { isCrawl, renderName } from "@/utils/crawler"; |
| 20 | +import { pluralOf } from "@/utils/pluralize"; |
| 21 | + |
| 22 | +const styles = unsafeCSS(stylesheet); |
| 23 | + |
| 24 | +// FIXME Sometimes the API returns circular dependencies |
| 25 | +const dependenciesWithoutSelf = (item: ArchivedItem) => |
| 26 | + item.requiresCrawls.filter((id) => id !== item.id); |
| 27 | + |
| 28 | +/** |
| 29 | + * @cssPart tree |
| 30 | + */ |
| 31 | +@customElement("btrix-item-dependency-list") |
| 32 | +@localized() |
| 33 | +export class ItemDependencyTree extends BtrixElement { |
| 34 | + static styles = styles; |
| 35 | + |
| 36 | + @property({ type: String }) |
| 37 | + collectionId?: string; |
| 38 | + |
| 39 | + @property({ type: Array }) |
| 40 | + items?: ArchivedItem[]; |
| 41 | + |
| 42 | + @query("sl-tree") |
| 43 | + private readonly tree?: SlTree | null; |
| 44 | + |
| 45 | + private readonly timerIds: number[] = []; |
| 46 | + |
| 47 | + private readonly dependenciesMap = new Map< |
| 48 | + string, |
| 49 | + ArchivedItem | undefined |
| 50 | + >(); |
| 51 | + |
| 52 | + private readonly dependenciesTask = new Task(this, { |
| 53 | + task: async ([items], { signal }) => { |
| 54 | + if (!items?.length) return; |
| 55 | + |
| 56 | + const itemsMap = new Map(items.map((item) => [item.id, item])); |
| 57 | + const newIds: string[] = []; |
| 58 | + |
| 59 | + items.forEach((item) => { |
| 60 | + dependenciesWithoutSelf(item).forEach((id) => { |
| 61 | + if (!this.dependenciesMap.get(id)) { |
| 62 | + const cachedItem = itemsMap.get(id); |
| 63 | + if (cachedItem) { |
| 64 | + this.dependenciesMap.set(id, cachedItem); |
| 65 | + } else { |
| 66 | + newIds.push(id); |
| 67 | + } |
| 68 | + } |
| 69 | + }); |
| 70 | + }); |
| 71 | + |
| 72 | + if (!newIds.length) return; |
| 73 | + |
| 74 | + const query = queryString.stringify( |
| 75 | + { |
| 76 | + ids: newIds, |
| 77 | + }, |
| 78 | + { |
| 79 | + arrayFormat: "none", |
| 80 | + }, |
| 81 | + ); |
| 82 | + |
| 83 | + const { items: dependencies } = await this.api.fetch< |
| 84 | + APIPaginatedList<ArchivedItem> |
| 85 | + >(`/orgs/${this.orgId}/all-crawls?${query}`, { signal }); |
| 86 | + |
| 87 | + newIds.forEach((id) => { |
| 88 | + this.dependenciesMap.set( |
| 89 | + id, |
| 90 | + dependencies.find((item) => item.id === id), |
| 91 | + ); |
| 92 | + }); |
| 93 | + }, |
| 94 | + args: () => [this.items] as const, |
| 95 | + }); |
| 96 | + |
| 97 | + disconnectedCallback(): void { |
| 98 | + this.timerIds.forEach(window.clearTimeout); |
| 99 | + super.disconnectedCallback(); |
| 100 | + } |
| 101 | + |
| 102 | + render() { |
| 103 | + if (!this.items?.length) return; |
| 104 | + |
| 105 | + return html` |
| 106 | + <btrix-table |
| 107 | + style="--btrix-table-grid-template-columns: ${[ |
| 108 | + "[clickable-start] min-content", |
| 109 | + "repeat(4, auto) [clickable-end]", |
| 110 | + ].join(" ")}" |
| 111 | + > |
| 112 | + <btrix-table-head |
| 113 | + class="mb-2 [--btrix-table-cell-padding-x:var(--sl-spacing-x-small)]" |
| 114 | + > |
| 115 | + <btrix-table-header-cell> |
| 116 | + <span class="sr-only">${msg("Status")}</span> |
| 117 | + </btrix-table-header-cell> |
| 118 | + <btrix-table-header-cell class="pl-0" |
| 119 | + >${msg("Name")}</btrix-table-header-cell |
| 120 | + > |
| 121 | + <btrix-table-header-cell> |
| 122 | + ${msg("Dependencies")} |
| 123 | + </btrix-table-header-cell> |
| 124 | + <btrix-table-header-cell>${msg("Date")}</btrix-table-header-cell> |
| 125 | + <btrix-table-header-cell>${msg("Size")}</btrix-table-header-cell> |
| 126 | + <btrix-table-header-cell> |
| 127 | + <span class="sr-only">${msg("Actions")}</span> |
| 128 | + </btrix-table-header-cell> |
| 129 | + </btrix-table-head> |
| 130 | + <btrix-table-body |
| 131 | + class="divide-y rounded border [--btrix-table-cell-padding-x:var(--sl-spacing-x-small)] [--btrix-table-cell-padding-y:var(--sl-spacing-2x-small)]" |
| 132 | + > |
| 133 | + ${repeat(this.items, ({ id }) => id, this.renderItem)} |
| 134 | + </btrix-table-body> |
| 135 | + </btrix-table> |
| 136 | + `; |
| 137 | + } |
| 138 | + |
| 139 | + private readonly renderItem = (item: ArchivedItem) => { |
| 140 | + return html` |
| 141 | + <btrix-table-row |
| 142 | + id=${item.id} |
| 143 | + class="h-10 cursor-pointer select-none whitespace-nowrap transition-colors duration-fast focus-within:bg-neutral-50 hover:bg-neutral-50" |
| 144 | + > |
| 145 | + ${this.renderContent(item)} |
| 146 | + </btrix-table-row> |
| 147 | + `; |
| 148 | + }; |
| 149 | + |
| 150 | + private readonly renderContent = (item: ArchivedItem) => { |
| 151 | + const dependencies = dependenciesWithoutSelf(item); |
| 152 | + const crawled = isCrawl(item); |
| 153 | + const collectionId = this.collectionId; |
| 154 | + |
| 155 | + const date = (value: string) => |
| 156 | + this.localize.date(value, { |
| 157 | + month: "2-digit", |
| 158 | + year: "2-digit", |
| 159 | + day: "2-digit", |
| 160 | + hour: "2-digit", |
| 161 | + minute: "2-digit", |
| 162 | + }); |
| 163 | + |
| 164 | + return html` |
| 165 | +
|
| 166 | + <btrix-table-cell> |
| 167 | + ${collectionStatusIcon({ item, collectionId })} |
| 168 | + </btrix-table-cell> |
| 169 | + <btrix-table-cell class="pl-0" rowClickTarget="a"> |
| 170 | + <a class="" |
| 171 | + href=${ |
| 172 | + crawled |
| 173 | + ? `${this.navigate.orgBasePath}/${OrgTab.Workflows}/${item.cid}/${WorkflowTab.Crawls}/${item.id}#${"dependencies" as ArchivedItemSectionName}` |
| 174 | + : `${this.navigate.orgBasePath}/${OrgTab.Items}/${item.type}/${item.id}` |
| 175 | + } |
| 176 | + @click=${this.navigate.link} |
| 177 | + > |
| 178 | + ${renderName(item)} |
| 179 | + </a> |
| 180 | + </btrix-table-cell> |
| 181 | + <btrix-table-cell class="flex items-center gap-1.5 truncate"> |
| 182 | + ${ |
| 183 | + dependencies.length |
| 184 | + ? html` |
| 185 | + ${dedupeIcon({ hasDependencies: true, hasDependents: true })} |
| 186 | + ${this.localize.number(dependencies.length)} |
| 187 | + ${pluralOf("dependencies", dependencies.length)} |
| 188 | + ` |
| 189 | + : nothing |
| 190 | + } |
| 191 | + </btrix-table-cell> |
| 192 | +
|
| 193 | + <btrix-table-cell class="flex items-center gap-1.5 truncate"> |
| 194 | + ${ |
| 195 | + crawled |
| 196 | + ? html`<sl-tooltip |
| 197 | + content=${msg("Date Finished")} |
| 198 | + placement="left" |
| 199 | + hoist |
| 200 | + > |
| 201 | + ${item.finished |
| 202 | + ? html`<sl-icon name="gear-wide-connected"></sl-icon> ${date( |
| 203 | + item.finished, |
| 204 | + )}` |
| 205 | + : html`<sl-icon name="play"></sl-icon> ${date(item.started)}`} |
| 206 | + </sl-tooltip>` |
| 207 | + : html`<sl-tooltip |
| 208 | + content=${msg("Date Uploaded")} |
| 209 | + placement="left" |
| 210 | + hoist |
| 211 | + > |
| 212 | + <sl-icon name="upload"></sl-icon> |
| 213 | + ${date(item.started)} |
| 214 | + </sl-tooltip>` |
| 215 | + } |
| 216 | + </btrix-table-cell> |
| 217 | +
|
| 218 | + <btrix-table-cell class="flex items-center gap-1.5 truncate"> |
| 219 | + <sl-icon name="file-earmark-binary"></sl-icon> |
| 220 | + ${this.localize.bytes(item.fileSize || 0, { unitDisplay: "short" })} |
| 221 | + </btrix-table-cell> |
| 222 | + </div>`; |
| 223 | + }; |
| 224 | +} |
0 commit comments