start first steps
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,18 @@ namespace zero.Core.Extensions
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Validate an email
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, string> Url<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
return ruleBuilder.Must((root, value, context) =>
|
||||
{
|
||||
return value.IsNullOrWhiteSpace() || Uri.IsWellFormedUriString(value, UriKind.Absolute);
|
||||
}).WithMessage("@errors/invalid_uri");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Validate an email
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
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();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<button class="ui-button" :class="{ themeClass }">
|
||||
<button class="ui-button" :class="typeClass">
|
||||
<span v-html="label"></span>
|
||||
</button>
|
||||
</template>
|
||||
@@ -18,7 +18,7 @@
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
theme: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'action'
|
||||
},
|
||||
@@ -26,9 +26,9 @@
|
||||
},
|
||||
|
||||
computed: {
|
||||
themeClass()
|
||||
typeClass()
|
||||
{
|
||||
return 'theme-' + this.theme;
|
||||
return 'type-' + this.type;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -42,6 +42,13 @@
|
||||
{
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
& + .ui-property
|
||||
{
|
||||
padding-top: 25px;
|
||||
margin-top: 25px;
|
||||
border-top: 1px solid var(--color-line);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-property.is-vertical
|
||||
@@ -91,11 +98,4 @@
|
||||
{
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.ui-property + .ui-property
|
||||
{
|
||||
padding-top: 25px;
|
||||
margin-top: 25px;
|
||||
border-top: 1px solid var(--color-line);
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,8 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core;
|
||||
using zero.Core.Entities.Setup;
|
||||
using zero.Web.Controllers;
|
||||
|
||||
namespace zero.Web.Setup
|
||||
@@ -17,5 +19,11 @@ namespace zero.Web.Setup
|
||||
{
|
||||
return View("/Views/Setup.cshtml");
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Save(SetupSave model)
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,4 +15,9 @@ input, textarea
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
|
||||
&:focus
|
||||
{
|
||||
border: 1px solid var(--color-line-mid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
// line colors
|
||||
--color-line: #eaebec;
|
||||
--color-line-mid: #d7d8d9;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,9 +15,11 @@ button
|
||||
display: inline-block;
|
||||
color: white;
|
||||
background: var(--color-primary);
|
||||
padding: 0 30px;
|
||||
padding: 0 20px;
|
||||
min-width: 80px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
@@ -28,7 +30,6 @@ button
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border: none;
|
||||
margin-top: 2rem;
|
||||
will-change: transform, background, border-color;
|
||||
transition: background .2s, transform .2s, opacity .2s;
|
||||
overflow: hidden;
|
||||
@@ -49,6 +50,21 @@ button
|
||||
pointer-events: none;
|
||||
//background: var(--color-dark);
|
||||
}
|
||||
|
||||
&.type-outline
|
||||
{
|
||||
background: var(--color-bg);
|
||||
color: var(--color-fg);
|
||||
border: 1px solid var(--color-line);
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
& + .ui-button
|
||||
{
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
|
||||
@import "canvas";
|
||||
|
||||
@import "steps";
|
||||
@import "canvas";
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
|
||||
.setup
|
||||
{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.setup-step
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
.setup-step h2
|
||||
{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.setup-step p
|
||||
{
|
||||
color: var(--color-fg-mid);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
margin: 0 0 2rem;
|
||||
}
|
||||
|
||||
.setup-step p b
|
||||
{
|
||||
color: var(--color-fg);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.setup-step .ui-property + .ui-property
|
||||
{
|
||||
padding-top: 0;
|
||||
border-top: none;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="setup-step setup-step-application">
|
||||
<h2>your application</h2>
|
||||
<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" type="text" class="ui-input" maxlength="40" placeholder="Enter name" />
|
||||
</ui-property>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import UiProperty from 'zerocomponents/forms/property.vue'
|
||||
import UiButton from 'zerocomponents/buttons/button.vue'
|
||||
|
||||
export default {
|
||||
name: 'setupStepApplication',
|
||||
|
||||
props: {
|
||||
value: String
|
||||
},
|
||||
|
||||
components: { UiProperty, UiButton },
|
||||
|
||||
mounted ()
|
||||
{
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="setup-step setup-step-database">
|
||||
<h2>database configuration</h2>
|
||||
<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.Url" type="text" class="ui-input" placeholder="Enter URL to the database" />
|
||||
</ui-property>
|
||||
|
||||
<ui-property label="Database name" :vertical="true">
|
||||
<input v-model="value.Name" type="text" class="ui-input" placeholder="Name of the database" />
|
||||
</ui-property>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import UiProperty from 'zerocomponents/forms/property.vue'
|
||||
import UiButton from 'zerocomponents/buttons/button.vue'
|
||||
|
||||
export default {
|
||||
name: 'setupStepDatabase',
|
||||
|
||||
props: {
|
||||
value: Object
|
||||
},
|
||||
|
||||
components: { UiProperty, UiButton },
|
||||
|
||||
mounted ()
|
||||
{
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -4,7 +4,7 @@
|
||||
<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 type="text" class="ui-input" placeholder="What's your name?" />
|
||||
<input type="text" class="ui-input" maxlength="40" placeholder="What's your name?" />
|
||||
</ui-property>
|
||||
|
||||
<ui-property label="Email" :vertical="true">
|
||||
@@ -12,7 +12,7 @@
|
||||
</ui-property>
|
||||
|
||||
<ui-property label="Password" :vertical="true">
|
||||
<input type="password" class="ui-input" autocomplete="off" placeholder="As long as you want, but at last 8 characters" />
|
||||
<input type="password" class="ui-input" autocomplete="off" maxlength="1024" placeholder="As long as you want but at last 8 characters" />
|
||||
</ui-property>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
<template>
|
||||
<div class="app setup">
|
||||
<step-user v-model="model.User" />
|
||||
<ui-button label="Next" />
|
||||
<step-database v-model="model.Database" />
|
||||
<div class="setup-buttons">
|
||||
<ui-button type="outline" label="Go back" />
|
||||
<ui-button label="Next" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import StepUser from './Steps/step-user.vue'
|
||||
import Sass from '../Sass/setup.scss'
|
||||
import UiButton from 'zerocomponents/buttons/button.vue'
|
||||
import StepUser from './Steps/step-user.vue'
|
||||
import StepApplication from './Steps/step-application.vue'
|
||||
import StepDatabase from './Steps/step-database.vue'
|
||||
|
||||
export default {
|
||||
name: 'setup',
|
||||
|
||||
components: { StepUser, UiButton },
|
||||
components: { UiButton, StepUser, StepApplication, StepDatabase },
|
||||
|
||||
data: () => ({
|
||||
model: {
|
||||
AppName: null,
|
||||
Part: null,
|
||||
User: {
|
||||
Name: null,
|
||||
Email: null,
|
||||
@@ -39,7 +45,53 @@
|
||||
|
||||
methods: {
|
||||
|
||||
save()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
.setup
|
||||
{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.setup-step
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
.setup-step h2
|
||||
{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.setup-step p
|
||||
{
|
||||
color: var(--color-fg-mid);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
margin: 0 0 2rem;
|
||||
}
|
||||
|
||||
.setup-step p b
|
||||
{
|
||||
color: var(--color-fg);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.setup-step .ui-property + .ui-property
|
||||
{
|
||||
padding-top: 0;
|
||||
border-top: none;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user