Are you sure you want to delete this search?
`, + actionHandler: this.handleDelete.bind(this), + primaryButtonText: 'Delete', + buttonVariant: 'danger', + }, + }; + isOpen(): boolean { + return this._dialog?.open ?? false; + } + async open( + operation: OperationType, + savedSearch?: UserSavedSearch, + overviewPageQueryInput?: string, + ) { + this.savedSearch = savedSearch; + this.operation = operation; + this.existingOverviewPageQuery = overviewPageQueryInput; + await this._dialog?.show(); + } + + async close() { + this._currentTask = undefined; + this.existingOverviewPageQuery = undefined; + this.savedSearch = undefined; + await this._dialog?.hide(); + } + + async handleSave() { + const isNameValid = this.nameInput!.reportValidity(); + const isDescriptionValid = this.descriptionInput!.reportValidity(); + const isQueryValid = this.isQueryValid(); + if (isNameValid && isDescriptionValid && isQueryValid) { + await this.editorAlert?.hide(); + this._currentTask = new Task(this, { + autoRun: false, + task: async ([name, description, query, user, apiClient]) => { + const token = await user!.getIdToken(); + return apiClient!.createSavedSearch(token, { + name: name, + description: description !== '' ? description : undefined, + query: query, + }); + }, + args: () => [ + this.nameInput!.value, + this.descriptionInput!.value, + this.queryInput!.value, + this.user, + this.apiClient, + ], + onComplete: async result => { + this.dispatchEvent( + new CustomEvent('saved-search-saved', { + detail: result, + bubbles: true, + composed: true, + }), + ); + await this.close(); + }, + onError: async (error: unknown) => { + let message: string; + if (error instanceof ApiError) { + message = error.message; + } else { + message = + 'Unknown error saving saved search. Check console for details.'; + console.error(error); + } + await new Toast().toast(message, 'danger', 'exclamation-triangle'); + }, + }); + await this._currentTask.run(); + } else { + await this.editorAlert?.show(); + } + } + + isQueryValid(): boolean { + if (this.queryInput) { + // TODO: Figure out a way to configure the form constraints on typeahead constraint + // I also tried to set the constraints up in the firstUpdated callback but the child is not rendered yet. + // Also, setting the custom validity message does not work because the typeahead renders the sl-input in a shadow DOM. + // Moving the typeahead to the light dom with createRenderRoot messes up the style of the dropdown. + // Until then, check manually. + // Once that is resolved, we can get rid of the sl-alert component below. + if ( + this.queryInput.value.length < + SavedSearchInputConstraints.QueryMinLength || + this.queryInput.value.length > + SavedSearchInputConstraints.QueryMaxLength + ) { + return false; + } else { + return true; + } + } + + return false; + } + + async handleEdit() { + const isNameValid = this.nameInput!.reportValidity(); + const isDescriptionValid = this.descriptionInput!.reportValidity(); + const isQueryValid = this.isQueryValid(); + if (isNameValid && isDescriptionValid && isQueryValid && this.savedSearch) { + await this.editorAlert?.hide(); + this._currentTask = new Task(this, { + autoRun: false, + task: async ([ + savedSearch, + name, + description, + query, + user, + apiClient, + ]) => { + const token = await user.getIdToken(); + const update: UpdateSavedSearchInput = { + id: savedSearch.id, + name: name !== savedSearch.name ? name : undefined, + description: + description !== savedSearch.description && description !== '' + ? description + : undefined, + query: query !== savedSearch.query ? query : undefined, + }; + return apiClient!.updateSavedSearch(update, token); + }, + args: () => [ + this.savedSearch!, + this.nameInput!.value, + this.descriptionInput!.value, + this.queryInput!.value, + this.user, + this.apiClient, + ], + onComplete: async result => { + this.dispatchEvent( + new CustomEvent('saved-search-edited', { + detail: result, + bubbles: true, + composed: true, + }), + ); + await this.close(); + }, + onError: async (error: unknown) => { + let message: string; + if (error instanceof ApiError) { + message = error.message; + } else { + message = + 'Unknown error editing saved search. Check console for details.'; + console.error(error); + } + await new Toast().toast(message, 'danger', 'exclamation-triangle'); + }, + }); + await this._currentTask.run(); + } else { + await this.editorAlert?.show(); + } + } + + async handleDelete() { + this._currentTask = new Task(this, { + autoRun: false, + task: async ([savedSearchID, user, apiClient]) => { + const token = await user!.getIdToken(); + await apiClient!.removeSavedSearchByID(savedSearchID!, token); + return savedSearchID!; + }, + args: () => [this.savedSearch?.id, this.user, this.apiClient], + onComplete: async savedSearchID => { + this.dispatchEvent( + new CustomEvent('saved-search-deleted', { + detail: savedSearchID, + bubbles: true, + composed: true, + }), + ); + await this.close(); + }, + onError: async (error: unknown) => { + let message: string; + if (error instanceof ApiError) { + message = error.message; + } else { + message = + 'Unknown error deleting saved search. Check console for details.'; + console.error(error); + } + await new Toast().toast(message, 'danger', 'exclamation-triangle'); + }, + }); + await this._currentTask.run(); + } + + async handleCancel() { + this.dispatchEvent( + new CustomEvent('saved-search-cancelled', { + bubbles: true, + composed: true, + }), + ); + await this.close(); + } + + renderForm(inProgress: boolean): TemplateResult { + let query: string; + if (this.existingOverviewPageQuery !== undefined) { + query = this.existingOverviewPageQuery; + } else if (this.savedSearch) { + query = this.savedSearch.query; + } else { + query = ''; + } + return html` +