Installer state management and now uses api

This commit is contained in:
JesmoDev
2022-05-30 16:10:01 +02:00
parent 6d222c219c
commit 6e9e46e814
3 changed files with 171 additions and 94 deletions

View File

@@ -1,7 +1,8 @@
import { css, CSSResultGroup, html, LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { customElement, property, state } from 'lit/decorators.js';
import { UUIBooleanInputEvent, UUISelectElement } from '@umbraco-ui/uui';
import { PostInstallRequest, UmbracoInstallerDatabaseModel } from '../models';
import { UUISelectElement } from '@umbraco-ui/uui';
@customElement('umb-installer-database')
export class UmbInstallerDatabase extends LitElement {
static styles: CSSResultGroup = [
@@ -34,14 +35,29 @@ export class UmbInstallerDatabase extends LitElement {
`,
];
options = [
{ name: 'SQLite', value: 'sqlite', selected: true },
{ name: 'Microsoft SQL Server', value: 'msqls' },
{ name: 'Custom connection string', value: 'custom' },
];
@property({ attribute: false })
public get databases(): UmbracoInstallerDatabaseModel[] | undefined {
return this._databases;
}
public set databases(value: UmbracoInstallerDatabaseModel[] | undefined) {
const oldValue = value;
this._databases = value;
this._options = value?.map((x, i) => ({ name: x.displayName, value: x.id, selected: i === 0 }));
this._selectedDatabase = value?.[0];
this.requestUpdate('databases', oldValue);
}
@state()
databaseType = 'sqlite';
private _options?: { name: string; value: string }[];
@state()
private _databases?: UmbracoInstallerDatabaseModel[] = [];
@state()
private _selectedDatabase?: UmbracoInstallerDatabaseModel;
@state()
private _useIntegratedAuthentication = false; // Used to hide credentials when integrated authentication is selected
private _handleSubmit = (e: SubmitEvent) => {
e.preventDefault();
@@ -52,37 +68,54 @@ export class UmbInstallerDatabase extends LitElement {
const isValid = form.checkValidity();
if (!isValid) return;
const formData = new FormData(form);
const database: Record<string, FormDataEntryValue> = {};
this.dispatchEvent(new CustomEvent('install', { bubbles: true, composed: true }));
const formData = new FormData(form);
for (const pair of formData.entries()) {
database[pair[0]] = pair[1];
}
this.dispatchEvent(new CustomEvent('submit', { detail: { database } }));
};
private _onBack() {
this.dispatchEvent(new CustomEvent('user', { bubbles: true, composed: true }));
this.dispatchEvent(new CustomEvent('previous', { bubbles: true, composed: true }));
}
private _renderSettings() {
switch (this.databaseType) {
case 'msqls':
return this._renderSqlServer();
case 'sqlite':
return this._renderSQLite();
default:
return this._renderCustom();
if (!this._selectedDatabase) return;
if (this._selectedDatabase.providerName === 'Microsoft.Data.SQLite') {
return this._renderSQLite();
}
if (this._selectedDatabase.providerName === null) {
return this._renderCustom();
}
const result = [];
if (this._selectedDatabase.requiresServer) {
result.push(this._renderServer());
}
if (this._selectedDatabase.requiresCredentials) {
result.push(this._renderCredentials());
}
return result;
}
private _renderSQLite = () => html` <uui-form-layout-item>
<uui-label for="database-file-name" slot="label" required>Database file name</uui-label>
<uui-input
type="text"
id="database-file-name"
name="database-file-name"
name="databaseFileName"
value="Umbraco"
required
required-message="Database file name is required"></uui-input>
</uui-form-layout-item>`;
private _renderSqlServer = () => html`
private _renderServer = () => html`
<h4>Connection</h4>
<hr />
<uui-form-layout-item>
@@ -91,7 +124,7 @@ export class UmbInstallerDatabase extends LitElement {
type="text"
id="server"
name="server"
placeholder="(local)SQLEXPRESS"
.placeholder=${this._selectedDatabase?.serverPlaceholder || ''}
required
required-message="Server is required"></uui-input>
</uui-form-layout-item>
@@ -100,29 +133,48 @@ export class UmbInstallerDatabase extends LitElement {
<uui-input
type="text"
id="database-name"
name="database-name"
name="databaseName"
placeholder="umbraco-cms"
required
required-message="Database name is required"></uui-input>
</uui-form-layout-item>
`;
private _renderCredentials = () => html`
<h4>Credentials</h4>
<hr />
<uui-form-layout-item>
<uui-checkbox name="int-auth" label="int-auth">Use integrated authentication</uui-checkbox>
</uui-form-layout-item>
<uui-form-layout-item>
<uui-label for="username" slot="label" required>Username</uui-label>
<uui-input type="text" id="username" name="username" required required-message="Username is required"></uui-input>
<uui-checkbox
name="intAuth"
label="int-auth"
@change=${(e: UUIBooleanInputEvent) => (this._useIntegratedAuthentication = e.target.checked)}
.checked=${this._useIntegratedAuthentication}
>Use integrated authentication</uui-checkbox
>
</uui-form-layout-item>
<uui-form-layout-item>
<uui-label for="password" slot="label" required>Password</uui-label>
<uui-input-password
id="password"
name="password"
required
required-message="Password is required"></uui-input-password>
</uui-form-layout-item>
${!this._useIntegratedAuthentication
? html` <uui-form-layout-item>
<uui-label for="username" slot="label" required>Username</uui-label>
<uui-input
type="text"
id="username"
name="username"
required
required-message="Username is required"></uui-input>
</uui-form-layout-item>
<uui-form-layout-item>
<uui-label for="password" slot="label" required>Password</uui-label>
<uui-input
id="password"
name="password"
type="text"
autocomplete="new-password"
required
required-message="Password is required"></uui-input>
</uui-form-layout-item>`
: ''}
`;
private _renderCustom = () => html`
@@ -131,14 +183,14 @@ export class UmbInstallerDatabase extends LitElement {
<uui-textarea
type="text"
id="connection-string"
name="connection-string"
name="connectionString"
required
required-message="Connection string is required"></uui-textarea>
</uui-form-layout-item>
`;
private _handleDatabaseTypeChange = (e: CustomEvent) => {
this.databaseType = (e.target as UUISelectElement).value.toString();
this._selectedDatabase = this.databases?.find((db) => db.id === (e.target as UUISelectElement).value);
};
render() {
@@ -148,7 +200,11 @@ export class UmbInstallerDatabase extends LitElement {
<form id="database-form" name="database" @submit="${this._handleSubmit}">
<uui-form-layout-item>
<uui-label for="database-type" slot="label" required>Database type</uui-label>
<uui-select .options=${this.options} @change=${this._handleDatabaseTypeChange}></uui-select>
<uui-select
id="database-type"
name="databaseType"
.options=${this._options || []}
@change=${this._handleDatabaseTypeChange}></uui-select>
</uui-form-layout-item>
${this._renderSettings()}

View File

@@ -1,7 +1,6 @@
import { css, CSSResultGroup, html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import { postInstall } from '../api/fetcher';
import { customElement, property } from 'lit/decorators.js';
import { PostInstallRequest, UmbracoInstallerUserModel } from '../models';
@customElement('umb-installer-user')
export class UmbInstallerUser extends LitElement {
@@ -33,6 +32,12 @@ export class UmbInstallerUser extends LitElement {
`,
];
@property({ attribute: false })
public userModel?: UmbracoInstallerUserModel; //TODO: Use this to validate the form
@property({ attribute: false })
public data?: PostInstallRequest;
private _handleSubmit = (e: SubmitEvent) => {
e.preventDefault();
@@ -42,58 +47,42 @@ export class UmbInstallerUser extends LitElement {
const isValid = form.checkValidity();
if (!isValid) return;
const user: Record<string, FormDataEntryValue> = {};
const formData = new FormData(form);
for (const pair of formData.entries()) {
user[pair[0]] = pair[1];
}
const name = formData.get('name') as string;
const email = formData.get('email') as string;
const password = formData.get('password') as string;
const news = formData.has('news');
this._next(name, email, password, news);
this.dispatchEvent(new CustomEvent('submit', { detail: user }));
};
private async _next(name: string, email: string, password: string, subscribeToNewsletter: boolean) {
console.log('Next', name, email, password, subscribeToNewsletter);
try {
await postInstall({
name,
email,
password,
telemetryLevel: 'Basic',
subscribeToNewsletter,
database: {
connectionString: '',
databaseProviderMetadataId: '1',
integratedAuth: false,
providerName: 'SQLite',
},
});
// TODO: Change to redirect when router has been added.
this.dispatchEvent(new CustomEvent('database', { bubbles: true, composed: true }));
} catch (error) {
console.log(error);
}
}
private _onCustomize() {
this.dispatchEvent(new CustomEvent('customize', { bubbles: true, composed: true }));
}
render() {
console.log('data?', this.data);
return html` <div class="uui-text">
<h1>Install Umbraco</h1>
<uui-form>
<form id="LoginForm" name="login" @submit="${this._handleSubmit}">
<uui-form-layout-item>
<uui-label for="name" slot="label" required>Name</uui-label>
<uui-input type="text" id="name" name="name" required required-message="Name is required"></uui-input>
<uui-input
type="text"
id="name"
.value=${this.data?.name || ''}
name="name"
required
required-message="Name is required"></uui-input>
</uui-form-layout-item>
<uui-form-layout-item>
<uui-label for="email" slot="label" required>Email</uui-label>
<uui-input type="email" id="email" name="email" required required-message="Email is required"></uui-input>
<uui-input
type="email"
id="email"
.value=${this.data?.email || ''}
name="email"
required
required-message="Email is required"></uui-input>
</uui-form-layout-item>
<uui-form-layout-item>
@@ -101,12 +90,16 @@ export class UmbInstallerUser extends LitElement {
<uui-input-password
id="password"
name="password"
.value=${this.data?.password || ''}
required
required-message="Password is required"></uui-input-password>
</uui-form-layout-item>
<uui-form-layout-item id="news-checkbox">
<uui-checkbox name="persist" label="Remember me">
<uui-checkbox
name="subscribeToNewsletter"
label="Remember me"
.checked=${this.data?.subscribeToNewsletter || false}>
Keep me updated on Umbraco Versions, Security Bulletins and Community News
</uui-checkbox>
</uui-form-layout-item>

View File

@@ -6,54 +6,82 @@ import './installer-user.element';
import { css, CSSResultGroup, html, LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { getInstall } from '../api/fetcher';
import { getInstall, postInstall } from '../api/fetcher';
import { PostInstallRequest, UmbracoInstaller } from '../models';
@customElement('umb-installer')
export class UmbInstaller extends LitElement {
static styles: CSSResultGroup = [css``];
@state()
step = 2;
step = 1;
@state()
user = {};
@state()
database = {};
data?: PostInstallRequest;
private _handleSubmit(e: CustomEvent) {
this.data = { ...this.data, ...e.detail };
this._goToNextStep();
console.log('data', this.data);
}
@state()
installerSettings?: UmbracoInstaller;
private _renderSection() {
switch (this.step) {
case 2:
return html`<umb-installer-database></umb-installer-database>`;
return html`<umb-installer-database
.databases=${this.installerSettings?.databases}
.data=${this.data}
@submit=${this._handleSubmit}></umb-installer-database>`;
case 3:
return html`<umb-installer-installing></umb-installer-installing>`;
default:
return html`<umb-installer-user></umb-installer-user>`;
return html`<umb-installer-user
.userModel=${this.installerSettings?.user}
.data=${this.data}
@submit=${this._handleSubmit}></umb-installer-user>`;
}
}
connectedCallback(): void {
super.connectedCallback();
this.addEventListener('install', () => this._handleInstall());
this.addEventListener('database', () => this._handleDatabase());
this.addEventListener('user', () => this._handleUser());
this.addEventListener('next', () => this._goToNextStep());
this.addEventListener('previous', () => this._goToPreviousStep());
getInstall({}).then(({ data }) => {
console.log('install data', data);
this.installerSettings = data;
console.log('Install data response', data);
});
}
private _handleUser() {
this.step = 1;
private _goToNextStep() {
//TODO: Fix with router
if (this.step === 2) {
if (this.data) {
this._postInstall(this.data);
}
} else {
this.step++;
}
}
private _handleDatabase() {
this.step = 2;
private _goToPreviousStep() {
this.step--;
}
private _handleInstall() {
this.step = 3;
private async _postInstall(data: PostInstallRequest) {
try {
await postInstall(data);
this.step = 3; //TODO: Fix with router
} catch (error) {
console.log(error);
}
}
render() {