feat: Allow superadmins to delete org (#1788)
Resolves https://github.com/webrecorder/browsertrix/issues/1453 <!-- Fixes #issue_number --> ### Changes Allows super-admins to delete an org via UI
This commit is contained in:
parent
5aa0ab62cb
commit
020c9dc1b8
@ -1,8 +1,8 @@
|
||||
import { localized, msg, str } from "@lit/localize";
|
||||
import {
|
||||
// type SlChangeEvent,
|
||||
type SlInput,
|
||||
// type SlMenuItem,
|
||||
import type {
|
||||
SlButton,
|
||||
SlChangeEvent,
|
||||
SlInput,
|
||||
} from "@shoelace-style/shoelace";
|
||||
import { serialize } from "@shoelace-style/shoelace/dist/utilities/form.js";
|
||||
import { css, html, nothing } from "lit";
|
||||
@ -52,6 +52,12 @@ export class OrgsList extends TailwindElement {
|
||||
@query("#orgReadOnlyDialog")
|
||||
private readonly orgReadOnlyDialog?: Dialog | null;
|
||||
|
||||
@query("#orgDeleteDialog")
|
||||
private readonly orgDeleteDialog?: Dialog | null;
|
||||
|
||||
@query("#orgDeleteButton")
|
||||
private readonly orgDeleteButton?: SlButton | null;
|
||||
|
||||
private readonly api = new APIController(this);
|
||||
private readonly navigate = new NavigateController(this);
|
||||
private readonly notify = new NotifyController(this);
|
||||
@ -86,6 +92,7 @@ export class OrgsList extends TailwindElement {
|
||||
</btrix-table>
|
||||
|
||||
${this.renderOrgQuotas()} ${this.renderOrgReadOnly()}
|
||||
${this.renderOrgDelete()}
|
||||
`;
|
||||
}
|
||||
|
||||
@ -157,7 +164,7 @@ export class OrgsList extends TailwindElement {
|
||||
<p class="mb-3">
|
||||
${msg(
|
||||
html`Are you sure you want to make
|
||||
<span class="font-semibold">${org.name}</span>
|
||||
<strong class="font-semibold">${org.name}</strong>
|
||||
read-only? Members will no longer be able to crawl, upload
|
||||
files, create browser profiles, or create collections.`,
|
||||
)}
|
||||
@ -208,6 +215,119 @@ export class OrgsList extends TailwindElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private renderOrgDelete() {
|
||||
return html`
|
||||
<btrix-dialog
|
||||
class="[--width:36rem]"
|
||||
id="orgDeleteDialog"
|
||||
.label=${msg(str`Confirm Org Deletion: ${this.currOrg?.name || ""}`)}
|
||||
@sl-after-hide=${() => (this.currOrg = null)}
|
||||
>
|
||||
${when(this.currOrg, (org) => {
|
||||
const confirmationStr = msg(str`Delete ${org.name}`);
|
||||
return html`
|
||||
<p class="mb-3">
|
||||
${msg(
|
||||
html`Are you sure you want to delete
|
||||
<strong class="font-semibold">${org.name}</strong>? This
|
||||
cannot be undone.`,
|
||||
)}
|
||||
</p>
|
||||
<ul class="mb-3 text-neutral-600">
|
||||
<li>
|
||||
${msg(str`Slug:`)}
|
||||
<a
|
||||
class="font-semibold text-primary hover:text-primary-500"
|
||||
href="/orgs/${org.slug}"
|
||||
target="_blank"
|
||||
>
|
||||
${org.slug}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
${msg("Members:")}
|
||||
<a
|
||||
class="font-semibold text-primary hover:text-primary-500"
|
||||
href="/orgs/${org.slug}/settings/members"
|
||||
target="_blank"
|
||||
>
|
||||
${formatNumber(Object.keys(org.users || {}).length)}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="mb-3">
|
||||
${msg(
|
||||
html`Deleting an org will delete all
|
||||
<strong class="font-semibold">
|
||||
<sl-format-bytes value=${org.bytesStored}></sl-format-bytes>
|
||||
</strong>
|
||||
of data associated with the org.`,
|
||||
)}
|
||||
</p>
|
||||
<ul class="mb-3 text-neutral-600">
|
||||
<li>
|
||||
${msg(
|
||||
html`Crawls:
|
||||
<sl-format-bytes
|
||||
value=${org.bytesStoredCrawls}
|
||||
></sl-format-bytes>`,
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
${msg(
|
||||
html`Uploads:
|
||||
<sl-format-bytes
|
||||
value=${org.bytesStoredUploads}
|
||||
></sl-format-bytes>`,
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
${msg(
|
||||
html`Profiles:
|
||||
<sl-format-bytes
|
||||
value=${org.bytesStoredProfiles}
|
||||
></sl-format-bytes>`,
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
<sl-divider></sl-divider>
|
||||
<sl-input
|
||||
placeholder=${confirmationStr}
|
||||
@sl-input=${(e: SlChangeEvent) => {
|
||||
const { value } = e.target as SlInput;
|
||||
this.orgDeleteButton!.disabled = value !== confirmationStr;
|
||||
}}
|
||||
>
|
||||
<strong slot="label" class="font-semibold">
|
||||
${msg(str`Type "${confirmationStr}" to confirm`)}
|
||||
</strong>
|
||||
</sl-input>
|
||||
<div slot="footer" class="flex justify-between">
|
||||
<sl-button
|
||||
size="small"
|
||||
@click=${() => void this.orgDeleteDialog?.hide()}
|
||||
>
|
||||
${msg("Cancel")}
|
||||
</sl-button>
|
||||
<sl-button
|
||||
id="orgDeleteButton"
|
||||
size="small"
|
||||
variant="danger"
|
||||
disabled
|
||||
@click=${async () => {
|
||||
await this.deleteOrg(org);
|
||||
void this.orgDeleteDialog?.hide();
|
||||
}}
|
||||
>
|
||||
${msg("Delete Org")}
|
||||
</sl-button>
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
</btrix-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private onUpdateQuota(e: CustomEvent) {
|
||||
const inputEl = e.target as SlInput;
|
||||
const quotas = this.currOrg?.quotas;
|
||||
@ -288,6 +408,30 @@ export class OrgsList extends TailwindElement {
|
||||
}
|
||||
}
|
||||
|
||||
private async deleteOrg(org: OrgData) {
|
||||
try {
|
||||
await this.api.fetch(`/orgs/${org.id}`, this.authState!, {
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
this.orgList = this.orgList?.filter((o) => o.id !== org.id);
|
||||
|
||||
this.notify.toast({
|
||||
message: msg(str`Org "${org.name}" has been deleted.`),
|
||||
variant: "success",
|
||||
icon: "check2-circle",
|
||||
});
|
||||
} catch (e) {
|
||||
console.debug(e);
|
||||
|
||||
this.notify.toast({
|
||||
message: msg("Sorry, couldn't delete org at this time."),
|
||||
variant: "danger",
|
||||
icon: "exclamation-octagon",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private readonly renderOrg = (org: OrgData) => {
|
||||
if (!this.userInfo) return;
|
||||
|
||||
@ -419,6 +563,17 @@ export class OrgsList extends TailwindElement {
|
||||
${msg("Make Read-Only")}
|
||||
</sl-menu-item>
|
||||
`}
|
||||
<sl-divider></sl-divider>
|
||||
<sl-menu-item
|
||||
style="--sl-color-neutral-700: var(--danger)"
|
||||
@click=${() => {
|
||||
this.currOrg = org;
|
||||
void this.orgDeleteDialog?.show();
|
||||
}}
|
||||
>
|
||||
<sl-icon slot="prefix" name="trash3"></sl-icon>
|
||||
${msg("Delete Org")}
|
||||
</sl-menu-item>
|
||||
</sl-menu>
|
||||
</btrix-overflow-dropdown>
|
||||
</btrix-table-cell>
|
||||
|
Loading…
Reference in New Issue
Block a user