basic setup flow works
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using zero.Core.Extensions;
|
||||
|
||||
namespace zero.Core.Api
|
||||
{
|
||||
public class Alias
|
||||
{
|
||||
private const char HYPHEN = '-';
|
||||
|
||||
private const char PLUS = '+';
|
||||
|
||||
private const char AMPERSAND = '&';
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts a term to a safe alias (suitable for URLs)
|
||||
/// </summary>
|
||||
public static string Generate(string value)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
char previous = default;
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
// get character in lower case
|
||||
char character = char.ToLower(value[i]);
|
||||
char target;
|
||||
|
||||
// do not handle surrogates
|
||||
if (char.IsSurrogate(character))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// special replacements accents + umlauts
|
||||
if (character.TryReplaceAccent(out char[] replacement))
|
||||
{
|
||||
if (replacement.Length > 1)
|
||||
{
|
||||
output.Append(replacement);
|
||||
output.Remove(output.Length - 1, 1);
|
||||
}
|
||||
target = replacement[replacement.Length - 1];
|
||||
}
|
||||
// append character a-z, 0-9
|
||||
else if (character.IsAZor09())
|
||||
{
|
||||
target = character;
|
||||
}
|
||||
// + sign for + and &
|
||||
else if (character == PLUS || character == AMPERSAND)
|
||||
{
|
||||
target = PLUS;
|
||||
}
|
||||
// add hyphen for all other characters
|
||||
else
|
||||
{
|
||||
target = HYPHEN;
|
||||
}
|
||||
|
||||
// add default characters
|
||||
if (target != HYPHEN && target != PLUS)
|
||||
{
|
||||
output.Append(target);
|
||||
}
|
||||
// add hyphen if it isn't first and previous char is not + or -
|
||||
else if (target == HYPHEN && previous != default && previous != PLUS && previous != HYPHEN)
|
||||
{
|
||||
output.Append(target);
|
||||
}
|
||||
// add plus. do remove hyphen it is the previous character
|
||||
else if (target == PLUS)
|
||||
{
|
||||
if (previous == HYPHEN)
|
||||
{
|
||||
output.Remove(output.Length - 1, 1);
|
||||
}
|
||||
output.Append(target);
|
||||
}
|
||||
|
||||
if (output.Length > 0)
|
||||
{
|
||||
previous = output[output.Length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
if (output.Length > 0 && !output[output.Length - 1].IsAZor09())
|
||||
{
|
||||
output.Remove(output.Length - 1, 1);
|
||||
}
|
||||
|
||||
if (output.Length == 0)
|
||||
{
|
||||
output.Append(HYPHEN);
|
||||
}
|
||||
|
||||
return output.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using FluentValidation.Results;
|
||||
using Raven.Client.Documents;
|
||||
using Raven.Client.Documents.Conventions;
|
||||
using Raven.Client.Documents.Session;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Entities;
|
||||
using zero.Core.Entities.Setup;
|
||||
using zero.Core.Validation;
|
||||
|
||||
namespace zero.Core.Api
|
||||
{
|
||||
public class SetupApi : ISetupApi
|
||||
{
|
||||
public SetupApi()
|
||||
{
|
||||
//Raven =
|
||||
}
|
||||
|
||||
|
||||
public async Task<EntityChangeResult<SetupModel>> Install(SetupModel model)
|
||||
{
|
||||
ValidationResult validation = await new SetupModelValidator().ValidateAsync(model);
|
||||
|
||||
if (!validation.IsValid)
|
||||
{
|
||||
return EntityChangeResult<SetupModel>.Fail(validation);
|
||||
}
|
||||
|
||||
// test read & write permissions on folders
|
||||
// TODO
|
||||
|
||||
// create temporary instance of database
|
||||
DocumentStore raven = new DocumentStore()
|
||||
{
|
||||
Urls = model.Database.Url.Split(','),
|
||||
Database = model.Database.Name
|
||||
};
|
||||
|
||||
raven.Conventions.FindCollectionName = type =>
|
||||
{
|
||||
return Constants.Database.CollectionPrefix + DocumentConventions.DefaultGetCollectionName(type);
|
||||
};
|
||||
|
||||
raven.Initialize();
|
||||
|
||||
// create application
|
||||
Application app = new Application()
|
||||
{
|
||||
CreatedDate = DateTimeOffset.Now,
|
||||
IsActive = true,
|
||||
Name = model.AppName,
|
||||
Alias = Alias.Generate(model.AppName)
|
||||
};
|
||||
|
||||
using (IAsyncDocumentSession session = raven.OpenAsyncSession())
|
||||
{
|
||||
await session.StoreAsync(app);
|
||||
await session.SaveChangesAsync();
|
||||
}
|
||||
|
||||
raven.Dispose();
|
||||
|
||||
return EntityChangeResult<SetupModel>.Success(model);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public interface ISetupApi
|
||||
{
|
||||
Task<EntityChangeResult<SetupModel>> Install(SetupModel model);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
public static class Database
|
||||
{
|
||||
public const string SharedAppId = "shared";
|
||||
|
||||
public const string CollectionPrefix = "zero.";
|
||||
}
|
||||
|
||||
public static class Sections
|
||||
|
||||
@@ -4,7 +4,7 @@ using zero.Core.Attributes;
|
||||
|
||||
namespace zero.Core.Entities
|
||||
{
|
||||
[DebuggerDisplay("Id = {Id,nq}, Name = {Name}")]
|
||||
[DebuggerDisplay("Id = {Id,nq}, Name = {Name}, Alias = {Alias}")]
|
||||
public abstract class DatabaseEntity : IDatabaseEntity
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
@@ -18,6 +18,9 @@ namespace zero.Core.Entities
|
||||
/// <inheritdoc/>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Alias { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public uint Sort { get; set; }
|
||||
|
||||
@@ -46,6 +49,12 @@ namespace zero.Core.Entities
|
||||
/// </summary>
|
||||
string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique alias which can be used in the frontend and URLs
|
||||
/// As generating aliases from the name would not be unique we append an incremental number if the desired alias is not available anymore
|
||||
/// </summary>
|
||||
string Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sort order
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace zero.Core.Entities.Setup
|
||||
{
|
||||
public sealed class SetupModel
|
||||
{
|
||||
public string AppName { get; set; }
|
||||
|
||||
public SetupUserModel User { get; set; }
|
||||
|
||||
public SetupDatabaseModel Database { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public sealed class SetupUserModel
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
public sealed class SetupDatabaseModel
|
||||
{
|
||||
public string Url { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
namespace zero.Core.Entities.Setup
|
||||
{
|
||||
public sealed class SetupSave
|
||||
{
|
||||
public string AppName { get; set; }
|
||||
|
||||
public SetupUserSave User { get; set; }
|
||||
|
||||
public SetupDatabaseSave Database { get; set; }
|
||||
|
||||
public SetupSavePart Part { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public sealed class SetupUserSave
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
public sealed class SetupDatabaseSave
|
||||
{
|
||||
public string Url { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public enum SetupSavePart
|
||||
{
|
||||
ValidateUser = 0,
|
||||
ValidateApplication = 1,
|
||||
ValidateDatabase = 2,
|
||||
ValidateFolderAccess = 3,
|
||||
WriteSettings = 4,
|
||||
SaveData = 5
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace zero.Core.Extensions
|
||||
{
|
||||
public static class CharExtensions
|
||||
{
|
||||
private static Dictionary<char, char[]> accents { get; } = new Dictionary<char, char[]>()
|
||||
{
|
||||
{ 'ä', new char[2] { 'a', 'e' } },
|
||||
{ 'á', new char[1] { 'a' } },
|
||||
{ 'à', new char[1] { 'a' } },
|
||||
{ 'ó', new char[1] { 'o' } },
|
||||
{ 'ò', new char[1] { 'o' } },
|
||||
{ 'é', new char[1] { 'e' } },
|
||||
{ 'è', new char[1] { 'e' } },
|
||||
{ 'ú', new char[1] { 'u' } },
|
||||
{ 'ù', new char[1] { 'u' } },
|
||||
{ 'í', new char[1] { 'i' } },
|
||||
{ 'ì', new char[1] { 'i' } },
|
||||
{ 'ö', new char[2] { 'o', 'e' } },
|
||||
{ 'ü', new char[2] { 'u', 'e' } },
|
||||
{ 'ß', new char[2] { 's', 's' } },
|
||||
{ '&', new char[1] { '+' } }
|
||||
};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check if a character is from a-z, A-Z or 0-9
|
||||
/// </summary>
|
||||
public static bool IsAZor09(this char value)
|
||||
{
|
||||
return (value >= 0x41 && value <= 0x5A) || (value >= 0x61 && value <= 0x7a) || (value >= 0x30 && value <= 0x39);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check if a character is in ASCII range
|
||||
/// </summary>
|
||||
public static bool IsASCII(this char value)
|
||||
{
|
||||
return value < 128;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Replaces an accent or umlaut with the appropriate URL + file ready variant
|
||||
/// </summary>
|
||||
public static bool TryReplaceAccent(this char value, out char[] result)
|
||||
{
|
||||
if (!accents.ContainsKey(value))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
result = accents[value];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using FluentValidation;
|
||||
using zero.Core.Entities.Setup;
|
||||
using zero.Core.Extensions;
|
||||
|
||||
namespace zero.Core.Validation
|
||||
{
|
||||
public class SetupModelValidator : AbstractValidator<SetupModel>
|
||||
{
|
||||
public SetupModelValidator()
|
||||
{
|
||||
RuleFor(x => x.User).NotNull();
|
||||
RuleFor(x => x.User.Email).NotEmpty().Email();
|
||||
RuleFor(x => x.User.Name).MaximumLength(40).NotEmpty();
|
||||
RuleFor(x => x.User.Password).MaximumLength(1024); // TODO password policy
|
||||
|
||||
RuleFor(x => x.AppName).MaximumLength(40).NotEmpty();
|
||||
|
||||
RuleFor(x => x.Database.Url).NotEmpty().Url();
|
||||
RuleFor(x => x.Database.Name).NotEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using FluentValidation;
|
||||
using zero.Core.Entities.Setup;
|
||||
using zero.Core.Extensions;
|
||||
|
||||
namespace zero.Core.Validation
|
||||
{
|
||||
public class SetupSaveValidator : AbstractValidator<SetupSave>
|
||||
{
|
||||
public SetupSaveValidator()
|
||||
{
|
||||
When(x => x.Part == SetupSavePart.ValidateUser, () =>
|
||||
{
|
||||
RuleFor(x => x.User).NotNull();
|
||||
RuleFor(x => x.User.Email).NotEmpty().Email();
|
||||
RuleFor(x => x.User.Name).MaximumLength(40).NotEmpty();
|
||||
RuleFor(x => x.User.Password).MaximumLength(1024); // TODO password policy
|
||||
});
|
||||
|
||||
When(x => x.Part == SetupSavePart.ValidateApplication, () =>
|
||||
{
|
||||
RuleFor(x => x.AppName).MaximumLength(40).NotEmpty();
|
||||
});
|
||||
|
||||
When(x => x.Part == SetupSavePart.ValidateDatabase, () =>
|
||||
{
|
||||
RuleFor(x => x.Database.Url).NotEmpty().Url();
|
||||
RuleFor(x => x.Database.Name).NotEmpty();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core;
|
||||
using zero.Core.Api;
|
||||
using zero.Core.Entities;
|
||||
using zero.Core.Entities.Setup;
|
||||
using zero.Web.Controllers;
|
||||
|
||||
@@ -10,9 +12,12 @@ namespace zero.Web.Setup
|
||||
[AllowAnonymous]
|
||||
public class SetupController : BackofficeController
|
||||
{
|
||||
public SetupController(IZeroConfiguration config) : base(config)
|
||||
{
|
||||
protected ISetupApi Api { get; private set; }
|
||||
|
||||
|
||||
public SetupController(IZeroConfiguration config, ISetupApi api) : base(config)
|
||||
{
|
||||
Api = api;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
@@ -21,9 +26,10 @@ namespace zero.Web.Setup
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Save(SetupSave model)
|
||||
public async Task<IActionResult> Install([FromBody] SetupModel model)
|
||||
{
|
||||
return Ok();
|
||||
EntityChangeResult<SetupModel> result = await Api.Install(model);
|
||||
return Json(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
:root
|
||||
{
|
||||
// accent colors
|
||||
--color-primary: #00aea2;
|
||||
--color-primary: #f9c202;
|
||||
--color-secondary: #2f3a43;
|
||||
--color-negative: #d82853;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ $font-headline-name: 'TwCenMt';
|
||||
|
||||
@include font-face($font-headline-name, '/Assets/Fonts/TwCenMt/twcenmt-regular', 400);
|
||||
|
||||
@include font-face($font-headline-name, '/Assets/Fonts/TwCenMt/TwCenMt-bold', 700);
|
||||
@include font-face($font-headline-name, '/Assets/Fonts/TwCenMt/twcenmt-bold', 700);
|
||||
|
||||
%font
|
||||
{
|
||||
|
||||
@@ -38,14 +38,16 @@ body
|
||||
|
||||
.app
|
||||
{
|
||||
width: 100%;
|
||||
max-width: 520px;
|
||||
background: var(--color-bg);
|
||||
border-radius: 3px;
|
||||
min-height: 600px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 40px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.app-headline
|
||||
{
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 64px;
|
||||
font-weight: 400;
|
||||
margin: -10vh 0 8vh;
|
||||
}
|
||||
|
||||
body:before
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<p>With zero you can manage multiple applications with one installation. Start by adding your first application.</p>
|
||||
|
||||
<ui-property label="Application name" :vertical="true">
|
||||
<input v-model="value.AppName" type="text" class="ui-input" maxlength="40" placeholder="Enter name" />
|
||||
<input v-model="value.appName" type="text" class="ui-input" maxlength="40" placeholder="My super awesome app maybe?" />
|
||||
</ui-property>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
<p>Zero uses <b>RavenDB</b> as it’s database. If you want a more sophisticated setup (i.e. cluster) you can override the IDocumentStore service which is registered as a singleton.</p>
|
||||
|
||||
<ui-property label="Instance URL" :vertical="true">
|
||||
<input v-model="value.Database.Url" type="text" class="ui-input" placeholder="Enter URL to the database" />
|
||||
<input v-model="value.database.url" type="text" class="ui-input" placeholder="Enter URL to the Raven instance" />
|
||||
</ui-property>
|
||||
|
||||
<ui-property label="Database name" :vertical="true">
|
||||
<input v-model="value.Database.Name" type="text" class="ui-input" placeholder="Name of the database" />
|
||||
<input v-model="value.database.name" type="text" class="ui-input" placeholder="Name of the existing database" />
|
||||
</ui-property>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,28 +1,97 @@
|
||||
<template>
|
||||
<div class="setup-step setup-step-install">
|
||||
<div class="setup-step-finish-inner">
|
||||
<img src="/Icons/bird.svg" class="bird" alt="bird" width="100" />
|
||||
<div v-if="!errorMessage" class="setup-step-install-inner">
|
||||
<div class="setup-step-install-progress"><i :style="{ width: progress + '%' }"></i></div>
|
||||
<h2>Installing</h2>
|
||||
</div>
|
||||
<div v-if="errorMessage" class="setup-step-install-error">
|
||||
<div class="setup-step-install-error-main">
|
||||
<h2>Oh no!</h2>
|
||||
<p>The setup of <b>zero</b> failed with following error(s):</p>
|
||||
<pre class="setup-step-install-error-code"><code v-html="errorMessage"></code></pre>
|
||||
</div>
|
||||
<div class="setup-buttons">
|
||||
<ui-button type="outline" :click="onBack" label="Go back" caret="left" caret-position="left" />
|
||||
<ui-button label="Retry" :click="install" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import UiButton from 'zerocomponents/buttons/button.vue'
|
||||
import Axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'setupStepInstall',
|
||||
|
||||
props: {
|
||||
value: Object,
|
||||
onBack: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
onNext: {
|
||||
type: Function,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
components: { UiButton },
|
||||
|
||||
mounted ()
|
||||
data: () => ({
|
||||
errorMessage: null,
|
||||
progress: 0
|
||||
}),
|
||||
|
||||
mounted()
|
||||
{
|
||||
|
||||
this.install();
|
||||
},
|
||||
|
||||
methods: {
|
||||
install()
|
||||
{
|
||||
this.errorMessage = null;
|
||||
|
||||
let interval = setInterval(() =>
|
||||
{
|
||||
if (++this.progress >= 100)
|
||||
{
|
||||
this.onNext();
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 30);
|
||||
|
||||
Axios.post('/api/setup/install', this.value)
|
||||
.then(response =>
|
||||
{
|
||||
|
||||
})
|
||||
.catch((error) =>
|
||||
{
|
||||
// The request was made and the server responded with a status code that falls out of the range of 2xx
|
||||
if (error.response)
|
||||
{
|
||||
this.error(error.response.data);
|
||||
}
|
||||
// The request was made but no response was received
|
||||
else if (error.request)
|
||||
{
|
||||
this.error(error.request);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.error(error.message);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
error(error)
|
||||
{
|
||||
this.errorMessage = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,12 +104,74 @@
|
||||
display: grid;
|
||||
grid-template-rows: 1fr auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
.setup-step-install-inner
|
||||
{
|
||||
text-align: center;
|
||||
|
||||
.bird
|
||||
h2
|
||||
{
|
||||
margin-bottom: 30px;
|
||||
margin: 30px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.setup-step-install-progress
|
||||
{
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
width: 80%;
|
||||
height: 12px;
|
||||
border-radius: 6px;
|
||||
background: var(--color-bg-light);
|
||||
overflow: hidden;
|
||||
|
||||
i
|
||||
{
|
||||
background: var(--color-primary);
|
||||
transition: width 0.1s ease;
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.setup-step-install-error
|
||||
{
|
||||
display: grid;
|
||||
grid-template-rows: 1fr auto;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
h2
|
||||
{
|
||||
color: var(--color-negative);
|
||||
}
|
||||
|
||||
div
|
||||
{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.setup-step-install-error-code
|
||||
{
|
||||
background: var(--color-bg-light);
|
||||
padding: 20px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
width: 440px;
|
||||
max-height: 50vh;
|
||||
overflow: scroll;
|
||||
font-family: 'Lucida Console', Consolas, sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.setup-step-install-error-main
|
||||
{
|
||||
display: grid;
|
||||
grid-template-rows: auto auto 1fr;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
</style>
|
||||
@@ -4,15 +4,15 @@
|
||||
<p>First we need some basic credentials from you to create a login. This login is the solely <b>superadmin</b> which is required to change critical application data.</p>
|
||||
|
||||
<ui-property label="Name" :vertical="true">
|
||||
<input v-model="value.User.Name" type="text" class="ui-input" maxlength="40" placeholder="What's your name?" />
|
||||
<input v-model="value.user.name" type="text" class="ui-input" maxlength="40" placeholder="What's your name? It doesn't have to be your real name" />
|
||||
</ui-property>
|
||||
|
||||
<ui-property label="Email" :vertical="true">
|
||||
<input v-model="value.User.Email" type="text" class="ui-input" placeholder="Your email will be used as your login" />
|
||||
<input v-model="value.user.email" type="text" class="ui-input" placeholder="Your email will be used as your login" />
|
||||
</ui-property>
|
||||
|
||||
<ui-property label="Password" :vertical="true">
|
||||
<input v-model="value.User.Password" type="password" class="ui-input" autocomplete="off" maxlength="1024" placeholder="As long as you want but at last 8 characters" />
|
||||
<input v-model="value.user.password" type="password" class="ui-input" autocomplete="off" maxlength="1024" placeholder="From 8 characters to a galaxy far, far away" />
|
||||
</ui-property>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+39
-21
@@ -1,9 +1,12 @@
|
||||
<template>
|
||||
<div class="app setup">
|
||||
<component v-bind:is="steps[step]" v-model="model" />
|
||||
<div class="setup-buttons" v-if="step < 3">
|
||||
<ui-button v-if="step > 0" type="outline" label="Go back" :click="prev" caret="left" caret-position="left" />
|
||||
<ui-button label="Next" :click="next" />
|
||||
<div class="app">
|
||||
<h1 class="app-headline">zero</h1>
|
||||
<div class="setup">
|
||||
<component v-bind:is="steps[step]" v-model="model" :on-back="prev" :on-next="next" />
|
||||
<div class="setup-buttons" v-if="step < 3">
|
||||
<ui-button v-if="step > 0" type="outline" label="Go back" :click="prev" caret="left" caret-position="left" />
|
||||
<ui-button :label="nextLabel" :click="next" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -24,23 +27,30 @@
|
||||
components: { UiButton, StepUser, StepApplication, StepDatabase, StepInstall, StepFinish },
|
||||
|
||||
data: () => ({
|
||||
step: 3,
|
||||
steps: [ 'step-user', 'step-application', 'step-database', 'step-install', 'step-finish' ],
|
||||
model: {
|
||||
AppName: null,
|
||||
Part: null,
|
||||
User: {
|
||||
Name: null,
|
||||
Email: null,
|
||||
Password: null
|
||||
},
|
||||
Database: {
|
||||
Url: 'http://localhost:9800',
|
||||
Name: null
|
||||
}
|
||||
}
|
||||
step: 0,
|
||||
steps: ['step-user', 'step-database', 'step-application', 'step-install', 'step-finish'],
|
||||
model: { "appName": "Brothers", "user": { "name": "Tobi", "email": "yolo@brothers.studio", "password": "yolo" }, "database": { "url": "http://localhost:9800", "name": "zero" } }
|
||||
//model: {
|
||||
// appName: null,
|
||||
// user: {
|
||||
// name: null,
|
||||
// email: null,
|
||||
// password: null
|
||||
// },
|
||||
// database: {
|
||||
// url: 'http://localhost:9800',
|
||||
// name: null
|
||||
// }
|
||||
//}
|
||||
}),
|
||||
|
||||
computed: {
|
||||
nextLabel()
|
||||
{
|
||||
return this.step < 2 ? 'Next' : 'Install application';
|
||||
}
|
||||
},
|
||||
|
||||
mounted()
|
||||
{
|
||||
|
||||
@@ -52,7 +62,7 @@
|
||||
{
|
||||
if (this.step + 1 >= this.steps.length)
|
||||
{
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.step += 1;
|
||||
@@ -80,6 +90,14 @@
|
||||
display: grid;
|
||||
grid-template-rows: 1fr auto;
|
||||
align-items: stretch;
|
||||
max-width: 100%;
|
||||
width: 520px;
|
||||
background: var(--color-bg);
|
||||
border-radius: 3px;
|
||||
min-height: 600px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.setup-step
|
||||
|
||||
@@ -12,6 +12,7 @@ using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core;
|
||||
using zero.Core.Api;
|
||||
|
||||
namespace zero.Web
|
||||
{
|
||||
@@ -86,6 +87,9 @@ namespace zero.Web
|
||||
});
|
||||
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
|
||||
// TODO move registration into core
|
||||
services.AddTransient<ISetupApi, SetupApi>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-15
@@ -2,19 +2,5 @@
|
||||
|
||||
isDebug: process.env.NODE_ENV !== 'production',
|
||||
|
||||
apiBase: '/api',
|
||||
|
||||
countries: [
|
||||
{ value: 'austria', text: 'Austria' },
|
||||
{ value: 'germany', text: 'Germany' },
|
||||
{ value: 'usa', text: 'USA' }
|
||||
],
|
||||
|
||||
categories: [
|
||||
{ value: 'unknown', text: '' },
|
||||
{ value: 'socialInsurance', text: 'SVA' },
|
||||
{ value: 'tax', text: 'Tax' },
|
||||
{ value: 'payout', text: 'Payout' },
|
||||
{ value: 'payment', text: 'Payment' }
|
||||
]
|
||||
apiBase: '/api'
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
@@ -21,6 +21,7 @@
|
||||
<Content Remove="wwwroot/Temp/**" />
|
||||
<Content Remove="wwwroot/Media/**" />
|
||||
<Content Remove="web.release.config" />
|
||||
<None Remove="wipe.js" />
|
||||
<None Include="web.release.config" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user