diff --git a/frontend/src/components/orgs-list.ts b/frontend/src/components/orgs-list.ts index 6078e80c..657fb5aa 100644 --- a/frontend/src/components/orgs-list.ts +++ b/frontend/src/components/orgs-list.ts @@ -1,16 +1,19 @@ import { localized, msg, str } from "@lit/localize"; import type { SlInput } from "@shoelace-style/shoelace"; -import { type TemplateResult } from "lit"; -import { customElement, property } from "lit/decorators.js"; +import { html, type TemplateResult } from "lit"; +import { customElement, property, query } from "lit/decorators.js"; import { when } from "lit/directives/when.js"; +import { TailwindElement } from "@/classes/TailwindElement"; +import type { Dialog } from "@/components/ui/dialog"; +import { APIController } from "@/controllers/api"; +import { NavigateController } from "@/controllers/navigate"; import type { CurrentUser, UserOrg } from "@/types/user"; -import LiteElement, { html } from "@/utils/LiteElement"; import type { OrgData } from "@/utils/orgs"; @localized() @customElement("btrix-orgs-list") -export class OrgsList extends LiteElement { +export class OrgsList extends TailwindElement { @property({ type: Object }) userInfo?: CurrentUser; @@ -23,6 +26,15 @@ export class OrgsList extends LiteElement { @property({ type: Object }) currOrg?: OrgData | null = null; + @query("#orgDeleteDialog") + orgDeleteDialog?: Dialog | null; + + @query("#orgQuotaDialog") + orgQuotaDialog?: Dialog | null; + + private readonly api = new APIController(this); + private readonly navigate = new NavigateController(this); + render() { if (this.skeleton) { return this.renderSkeleton(); @@ -33,17 +45,108 @@ export class OrgsList extends LiteElement { return html` `; } + private renderOrgDelete() { + return html` + (this.currOrg = null)} + > + ${when(this.currOrg, (org) => { + const confirmationStr = msg(str`Delete ${org.name}`); + return html` +

+ ${msg( + html`Are you sure you want to delete + + ${org.name} + ? This cannot be undone.`, + )} +

+ +

+ ${msg( + html`Deleting an org will delete all + + + + of data associated with the org.`, + )} +

+ + + + + ${msg(str`Type "${confirmationStr}" to confirm`)} + + + `; + })} +
+ ${msg("Delete Org")} + +
+
+ `; + } + private renderOrgQuotas() { return html` (this.currOrg = null)} + @sl-after-hide=${() => (this.currOrg = null)} > ${when(this.currOrg?.quotas, (quotas) => Object.entries(quotas).map(([key, value]) => { @@ -72,6 +175,7 @@ export class OrgsList extends LiteElement { label = msg("Unlabeled"); } return html` { - e.preventDefault(); - e.stopPropagation(); - this.currOrg = org; - return false; - }; - - return stop; } private readonly renderOrg = (defaultOrg?: UserOrg) => (org: OrgData) => { @@ -158,12 +250,31 @@ export class OrgsList extends LiteElement { ? msg(`1 member`) : msg(str`${memberCount} members`)} - + e.stopPropagation()} + > + + { + this.currOrg = org; + void this.orgQuotaDialog?.show(); + }} + > + + ${msg("Edit Quotas")} + + { + this.currOrg = org; + void this.orgDeleteDialog?.show(); + }} + > + + ${msg("Delete Org")} + + + `; @@ -180,7 +291,7 @@ export class OrgsList extends LiteElement { } private makeOnOrgClick(org: OrgData) { - const navigate = () => this.navTo(`/orgs/${org.slug}`); + const navigate = () => this.navigate.to(`/orgs/${org.slug}`); if (typeof window.getSelection !== "undefined") { return () => { diff --git a/frontend/src/types/org.ts b/frontend/src/types/org.ts index 890ab5c0..d3020d94 100644 --- a/frontend/src/types/org.ts +++ b/frontend/src/types/org.ts @@ -19,6 +19,9 @@ export type OrgData = { slug: string; quotas?: Record; bytesStored: number; + bytesStoredCrawls: number; + bytesStoredUploads: number; + bytesStoredProfiles: number; usage: { [key: YearMonth]: number } | null; crawlExecSeconds?: { [key: YearMonth]: number }; monthlyExecSeconds?: { [key: YearMonth]: number };