Allow admin users to change org name (#506)

This commit is contained in:
sua yoo 2023-01-25 10:55:45 -08:00 committed by GitHub
parent 0486d50fe9
commit 7dc85cc33c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 126 additions and 7 deletions

View File

@ -583,6 +583,7 @@ export class App extends LiteElement {
class="w-full"
@navigate=${this.onNavigateTo}
@need-login=${this.onNeedLogin}
@update-user-info=${this.updateUserInfo}
@notify="${this.onNotify}"
.authState=${this.authService.authState}
.userInfo=${this.userInfo}
@ -681,7 +682,10 @@ export class App extends LiteElement {
tabName: "browser-profiles",
label: msg("Browser Profiles"),
})}
${this.renderNavTab({ tabName: "members", label: msg("Members") })}
${this.renderNavTab({
tabName: "settings",
label: msg("Org Settings"),
})}
</nav>
</div>

View File

@ -1,5 +1,7 @@
import { state, property } from "lit/decorators.js";
import { msg, localized, str } from "@lit/localize";
import { when } from "lit/directives/when.js";
import { serialize } from "@shoelace-style/shoelace/dist/utilities/form.js";
import type { ViewState } from "../../utils/APIRouter";
import type { AuthState } from "../../utils/AuthService";
@ -16,12 +18,13 @@ import "./crawls-list";
import "./browser-profiles-detail";
import "./browser-profiles-list";
import "./browser-profiles-new";
import { SlButton } from "@shoelace-style/shoelace";
export type OrgTab =
| "crawls"
| "crawl-configs"
| "browser-profiles"
| "members";
| "settings";
const defaultTab = "crawls";
@ -71,6 +74,12 @@ export class Org extends LiteElement {
@state()
private successfullyInvitedEmail?: string;
@state()
private isEditingOrgName = false;
@state()
private isSavingOrgName = false;
async willUpdate(changedProperties: Map<string, any>) {
if (changedProperties.has("orgId") && this.orgId) {
try {
@ -125,11 +134,11 @@ export class Org extends LiteElement {
case "browser-profiles":
tabPanelContent = this.renderBrowserProfiles();
break;
case "members":
case "settings":
if (this.isAddingMember) {
tabPanelContent = this.renderAddMember();
} else {
tabPanelContent = this.renderMembers();
tabPanelContent = this.renderOrgSettings();
}
break;
default:
@ -225,6 +234,74 @@ export class Org extends LiteElement {
></btrix-browser-profiles-list>`;
}
private renderOrgSettings() {
return html`<btrix-section-heading
>${msg("Org Information")}</btrix-section-heading
>
<section class="mt-5 mb-10">${this.renderOrgName()}</section>
<btrix-section-heading>${msg("Org Members")}</btrix-section-heading>
<section class="mt-5">${this.renderMembers()}</section>`;
}
private renderOrgName() {
if (!this.org || !this.userInfo) return;
const memberInfo = (this.org.users ?? {})[this.userInfo.id];
if (!memberInfo || !isOwner(memberInfo.role)) {
return html`
<sl-input
label=${msg("Org Name")}
value=${this.org.name}
readonly
></sl-input>
`;
}
return html`<form
@submit=${this.onOrgNameSubmit}
@reset=${() => (this.isEditingOrgName = false)}
>
<div class="flex items-end">
<div class="flex-1 mr-3">
<sl-input
name="orgName"
label=${msg("Org Name")}
autocomplete="off"
value=${this.org.name}
?readonly=${!this.isEditingOrgName}
?required=${this.isEditingOrgName}
></sl-input>
</div>
<div class="flex-0">
${when(
this.isEditingOrgName,
() => html`
<sl-button type="reset" class="mr-1">${msg("Cancel")}</sl-button>
<sl-button
type="submit"
variant="primary"
?disabled=${this.isSavingOrgName}
?loading=${this.isSavingOrgName}
>${msg("Save Changes")}</sl-button
>
`,
() => html`
<sl-button
@click=${(e: MouseEvent) => {
this.isEditingOrgName = true;
(e.target as SlButton)
.closest("form")
?.querySelector("sl-input")
?.focus();
}}
>${msg("Edit")}</sl-button
>
`
)}
</div>
</div>
</form>`;
}
private renderMembers() {
if (!this.org!.users) return;
@ -245,8 +322,7 @@ export class Org extends LiteElement {
<div class="text-right">
<sl-button
href=${`/orgs/${this.orgId}/members/add-member`}
variant="primary"
href=${`/orgs/${this.orgId}/settings/add-member`}
@click=${this.navLink}
>${msg("Add Member")}</sl-button
>
@ -298,7 +374,7 @@ export class Org extends LiteElement {
class="inline-block align-middle"
></sl-icon>
<span class="inline-block align-middle"
>${msg("Back to Members")}</span
>${msg("Back to Settings")}</span
>
</a>
</div>
@ -321,6 +397,45 @@ export class Org extends LiteElement {
return data;
}
private async onOrgNameSubmit(e: SubmitEvent) {
e.preventDefault();
if (!this.org) return;
const { orgName } = serialize(e.target as HTMLFormElement);
this.isSavingOrgName = true;
try {
await this.apiFetch(`/orgs/${this.org.id}/rename`, this.authState!, {
method: "POST",
body: JSON.stringify({ name: orgName }),
});
this.notify({
message: msg("Updated organization name."),
variant: "success",
icon: "check2-circle",
duration: 8000,
});
this.org = {
...this.org,
name: orgName as string,
};
this.isEditingOrgName = false;
this.dispatchEvent(new CustomEvent("update-user-info"));
} catch (e) {
console.debug(e);
this.notify({
message: msg("Sorry, couldn't update organization name at this time."),
variant: "danger",
icon: "exclamation-octagon",
});
}
this.isSavingOrgName = false;
}
onInviteSuccess(
event: CustomEvent<{ inviteEmail: string; isExistingUser: boolean }>
) {