import { state, property, customElement } from "lit/decorators.js"; import { msg, localized, str } from "@lit/localize"; import { serialize } from "@shoelace-style/shoelace/dist/utilities/form.js"; import type { AuthState } from "@/utils/AuthService"; import type { CurrentUser } from "@/types/user"; import type { OrgData } from "@/utils/orgs"; import LiteElement, { html } from "@/utils/LiteElement"; import type { APIPaginatedList } from "@/types/api"; import { maxLengthValidator } from "@/utils/form"; @localized() @customElement("btrix-home") export class Home extends LiteElement { @property({ type: Object }) authState?: AuthState; @property({ type: Object }) userInfo?: CurrentUser; @property({ type: String }) slug?: string; @state() private isInviteComplete?: boolean; @state() private orgList?: OrgData[]; @state() private isAddingOrg = false; @state() private isAddOrgFormVisible = false; @state() private isSubmittingNewOrg = false; private validateOrgNameMax = maxLengthValidator(50); connectedCallback() { if (this.authState) { super.connectedCallback(); } else { this.navTo("/log-in"); } } willUpdate(changedProperties: Map) { if (changedProperties.has("slug") && this.slug) { this.navTo(`/orgs/${this.slug}`); } else if (changedProperties.has("authState") && this.authState) { this.fetchOrgs(); } } async updated(changedProperties: Map) { const orgListUpdated = changedProperties.has("orgList") && this.orgList; const userInfoUpdated = changedProperties.has("userInfo") && this.userInfo; if (orgListUpdated || userInfoUpdated) { if (this.userInfo?.isAdmin && this.orgList && !this.orgList.length) { this.isAddingOrg = true; } } } render() { if (!this.userInfo || !this.orgList) { return html`
`; } let title: any; let content: any; if (this.userInfo.isAdmin === true) { title = msg("Welcome"); content = this.renderAdminOrgs(); } if (this.userInfo.isAdmin === false) { title = msg("Organizations"); content = this.renderLoggedInNonAdmin(); } return html`

${title}


${content}
`; } private renderAdminOrgs() { return html`
{ const formData = new FormData(e.target as HTMLFormElement); const id = formData.get("crawlId"); this.navTo(`/crawls/crawl/${id}`); }} >
${msg("Go to Crawl")}
${msg("Go")}

${msg("All Organizations")}

(this.isAddingOrg = true)} > ${msg("New Organization")}
org.default === true )} @update-quotas=${this.onUpdateOrgQuotas} >

${msg("Invite User to Org")}

${this.renderInvite()}
{ // Disable closing if there are no orgs if (this.orgList?.length) { this.isAddingOrg = false; } else { e.preventDefault(); } }} @sl-show=${() => (this.isAddOrgFormVisible = true)} @sl-after-hide=${() => (this.isAddOrgFormVisible = false)} > ${this.isAddOrgFormVisible ? html`
(this.isAddingOrg = false)} @submit=${this.onSubmitNewOrg} >
${this.orgList?.length ? html` ${msg("Cancel")} ` : ""} ${msg("Create Org")}
` : ""}
`; } private renderLoggedInNonAdmin() { if (this.orgList && !this.orgList.length) { return html`

${msg("You don't have any organizations.")}

`; } return html` `; } private renderInvite() { if (this.isInviteComplete) { return html` (this.isInviteComplete = false)} >${msg("Send another invite")} `; } const defaultOrg = this.userInfo?.orgs.find( (org) => org.default === true ) || { name: "" }; return html` (this.isInviteComplete = true)} > `; } private async fetchOrgs() { this.orgList = await this.getOrgs(); } private async getOrgs() { const data = await this.apiFetch>( "/orgs", this.authState! ); return data.items; } private async onSubmitNewOrg(e: SubmitEvent) { e.preventDefault(); const formEl = e.target as HTMLFormElement; if (!(await this.checkFormValidity(formEl))) return; const params = serialize(formEl); this.isSubmittingNewOrg = true; try { await this.apiFetch(`/orgs/create`, this.authState!, { method: "POST", body: JSON.stringify(params), }); this.fetchOrgs(); this.notify({ message: msg(str`Created new org named "${params.name}".`), variant: "success", icon: "check2-circle", duration: 8000, }); this.isAddingOrg = false; } catch (e: any) { this.notify({ message: e.isApiError ? e.message : msg("Sorry, couldn't create organization at this time."), variant: "danger", icon: "exclamation-octagon", }); } this.isSubmittingNewOrg = false; } async onUpdateOrgQuotas(e: CustomEvent) { const org = e.detail as OrgData; await this.apiFetch(`/orgs/${org.id}/quotas`, this.authState!, { method: "POST", body: JSON.stringify(org.quotas), }); } async checkFormValidity(formEl: HTMLFormElement) { await this.updateComplete; return !formEl.querySelector("[data-invalid]"); } }