Skip to content

Commit

Permalink
Design settings page (#1)
Browse files Browse the repository at this point in the history
* Update Settings.cshtml

* remove font awesome nuget

* add side nav

* fix form UiText translations

* fix translated text that is only the variable

* no fullwidth buttons on settings page, create FlowType

* fix forgot password button at password field

* set anchors to box divs

* only show default instructions when there is no other message

* use css file

* fix typo

* fix navigation items
  • Loading branch information
josxha authored Oct 25, 2023
1 parent 12869d5 commit 297e2d4
Show file tree
Hide file tree
Showing 21 changed files with 187 additions and 59 deletions.
46 changes: 44 additions & 2 deletions KratosSelfService/Extensions/GroupEnumExt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace KratosSelfService.Extensions;

public static class GroupEnumExt
{
public static string ToLocalString(this KratosUiNode.GroupEnum instance, IOryElementsTranslator oryTranslator)
public static string ToLocalTitleString(this KratosUiNode.GroupEnum instance, IOryElementsTranslator oryTranslator)
{
return instance switch
{
Expand All @@ -14,7 +14,49 @@ public static string ToLocalString(this KratosUiNode.GroupEnum instance, IOryEle
KratosUiNode.GroupEnum.LookupSecret => oryTranslator.Get("settings.title-lookup-secret"),
KratosUiNode.GroupEnum.Webauthn => oryTranslator.Get("settings.title-webauthn"),
KratosUiNode.GroupEnum.Oidc => oryTranslator.Get("settings.title-oidc"),
_ => instance.ToString()
_ => instance.ToString().ToLower()
};
}

public static string ToLocalNavString(this KratosUiNode.GroupEnum instance, IOryElementsTranslator oryTranslator)
{
return instance switch
{
KratosUiNode.GroupEnum.Password => oryTranslator.Get("settings.navigation-password"),
KratosUiNode.GroupEnum.Profile => oryTranslator.Get("settings.navigation-profile"),
KratosUiNode.GroupEnum.Totp => oryTranslator.Get("settings.navigation-totp"),
KratosUiNode.GroupEnum.LookupSecret => oryTranslator.Get("settings.navigation-backup-codes"),
KratosUiNode.GroupEnum.Webauthn => oryTranslator.Get("settings.navigation-webauthn"),
KratosUiNode.GroupEnum.Oidc => oryTranslator.Get("settings.navigation-oidc"),
_ => instance.ToString().ToLower()
};
}

public static string ToSiteAnchorTag(this KratosUiNode.GroupEnum instance)
{
return instance switch
{
KratosUiNode.GroupEnum.Password => "password",
KratosUiNode.GroupEnum.Profile => "profile",
KratosUiNode.GroupEnum.Totp => "totp",
KratosUiNode.GroupEnum.LookupSecret => "lookupSecret",
KratosUiNode.GroupEnum.Webauthn => "webauthn",
KratosUiNode.GroupEnum.Oidc => "oidc",
_ => instance.ToString().ToLower()
};
}

public static string ToFaIcon(this KratosUiNode.GroupEnum instance)
{
return instance switch
{
KratosUiNode.GroupEnum.Password => "fa-lock",
KratosUiNode.GroupEnum.Profile => "fa-user",
KratosUiNode.GroupEnum.Totp => "fa-shield",
KratosUiNode.GroupEnum.LookupSecret => "fa-list",
KratosUiNode.GroupEnum.Webauthn => "fa-list",
KratosUiNode.GroupEnum.Oidc => "fa-list",
_ => instance.ToString().ToLower()
};
}
}
17 changes: 14 additions & 3 deletions KratosSelfService/Models/models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,18 @@ public record KratosUiTextMessageModel(
string CssClass
);

public record KratosUiNodeModel(KratosUiNode node,
KratosLoginFlow? loginFlow = null,
public record KratosUiNodeModel(
KratosUiNode node,
FlowType FlowType,
string? forgotPasswordUrl = null
);
);

public enum FlowType
{
Settings,
Login,
Logout,
Registration,
Recovery,
Verification
}
14 changes: 12 additions & 2 deletions KratosSelfService/OryElementsTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,18 @@ public string Get(string text)
public string? ForUiText(KratosUiText? uiText)
{
if (uiText == null) return null;
if (uiText.Id == 1070002) return uiText.Text;
return Get($"identities.messages.{uiText.Id}");
return uiText.Id switch
{
// title
1070002 => uiText.Text,
// totp secrets
1050006 or 1050009 => uiText.Text,
// backup codes, secrets list
1050015 => uiText.Text,
// reason
4000001 or 5000001 => uiText.Text,
_ => Get($"identities.messages.{uiText.Id}")
};
}
}

Expand Down
3 changes: 3 additions & 0 deletions KratosSelfService/Resources/CustomTranslator.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,7 @@
<data name="sessions.currentSession" xml:space="preserve">
<value>Diese Sitzung</value>
</data>
<data name="Actions" xml:space="preserve">
<value>Aktionen</value>
</data>
</root>
3 changes: 3 additions & 0 deletions KratosSelfService/Resources/CustomTranslator.resx
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,7 @@
<data name="sessions.currentSession" xml:space="preserve">
<value>Current Session</value>
</data>
<data name="Actions" xml:space="preserve">
<value>Actions</value>
</data>
</root>
2 changes: 1 addition & 1 deletion KratosSelfService/Views/Login/Login.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
}
@foreach (var node in Model.flow.Ui.Nodes)
{
var model = new KratosUiNodeModel(node, Model.flow, Model.forgotPasswordUrl);
var model = new KratosUiNodeModel(node, FlowType.Login, Model.forgotPasswordUrl);
@await Component.InvokeAsync("KratosUiNodeInput", model)
}
</div>
Expand Down
2 changes: 1 addition & 1 deletion KratosSelfService/Views/Recovery/Recovery.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
}
@foreach (var node in Model.flow.Ui.Nodes)
{
var model = new KratosUiNodeModel(node);
var model = new KratosUiNodeModel(node, FlowType.Recovery);
@await Component.InvokeAsync("KratosUiNodeInput", model)
}
</form>
2 changes: 1 addition & 1 deletion KratosSelfService/Views/Registration/Registration.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
}
@foreach (var node in Model.flow.Ui.Nodes)
{
var model = new KratosUiNodeModel(node);
var model = new KratosUiNodeModel(node, FlowType.Registration);
@await Component.InvokeAsync("KratosUiNodeInput", model)
}
</div>
Expand Down
110 changes: 82 additions & 28 deletions KratosSelfService/Views/Settings/Settings.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,95 @@
@model SettingsModel
@{
ViewData["Title"] = OryTranslator.Get("settings.title");
Layout = "_CardLayout";
var allGroups = Model.flow.Ui.Nodes
.GroupBy(node => node.Group).ToList();
var defaultNodes = allGroups
.First(group => group.Key == KratosUiNode.GroupEnum.Default)
.ToArray();
var groups = allGroups
.Where(group => group.Key != KratosUiNode.GroupEnum.Default);
.Where(group => group.Key != KratosUiNode.GroupEnum.Default)
.ToArray();
}

<section class="section">
<h1 class="title">@OryTranslator.Get("settings.title")</h1>
<p>@OryTranslator.Get("settings.subtitle-instructions")</p>
@if (Model.flow.Ui.Messages != null)
{
foreach (var message in Model.flow.Ui.Messages)
{
@await Component.InvokeAsync("KratosUiTextMessage", message)
}
}
@foreach (var group in groups)
{
<hr/>
<h3 class="subtitle mt-3">@group.Key.ToLocalString(OryTranslator)</h3>
<form class="mb-3" action="@Model.postUri" method="post">
@foreach (var node in defaultNodes)
{
var model = new KratosUiNodeModel(node);
@await Component.InvokeAsync("KratosUiNodeInput", model)
}
@foreach (var node in group)
<div class="columns">
<div class="column is-3 is-hidden-mobile">
<div class="section">
<aside>
<p class="menu-label">@OryTranslator.Get("settings.title-navigation")</p>
<ul class="menu-list">
@foreach (var group in groups)
{
var groupType = group.Key;
<li>
<a href="#@groupType.ToSiteAnchorTag()">
<span class="icon-text">
<span class="icon">
<i class="fa-solid @groupType.ToFaIcon() fa-sm"></i>
</span>
<span>
@groupType.ToLocalNavString(OryTranslator)
</span>
</span>
</a>
</li>
}
</ul>
<p class="menu-label mt-5">@CustomTranslator.Get("Actions")</p>
<ul class="menu-list">
<li>
<a href="logout">
<span class="icon-text">
<span class="icon">
<i class="fa-solid fa-sign-out fa-sm"></i>
</span>
<span>
@OryTranslator.Get("settings.navigation-logout")
</span>
</span>
</a>
</li>
</ul>
</aside>
</div>
</div>
<div class="column is-9">
<div class="section">
<div class="box pt-5">
<h1 class="title">@OryTranslator.Get("settings.title")</h1>
@if ((Model.flow.Ui.Messages?.Count ?? 0) == 0)
{
<div class="message is-info p-3">
@OryTranslator.Get("settings.subtitle-instructions")
</div>
}
@if (Model.flow.Ui.Messages != null)
{
foreach (var message in Model.flow.Ui.Messages)
{
@await Component.InvokeAsync("KratosUiTextMessage", message)
}
}
</div>
@foreach (var group in groups)
{
var model = new KratosUiNodeModel(node);
@await Component.InvokeAsync("KratosUiNodeInput", model)
<div class="box p-5" id="@group.Key.ToSiteAnchorTag()">
<h3 class="subtitle">
@group.Key.ToLocalTitleString(OryTranslator)
</h3>
<form class="mb-3" action="@Model.postUri" method="post">
@foreach (var node in defaultNodes)
{
var model = new KratosUiNodeModel(node, FlowType.Settings);
@await Component.InvokeAsync("KratosUiNodeInput", model)
}
@foreach (var node in group)
{
var model = new KratosUiNodeModel(node, FlowType.Settings);
@await Component.InvokeAsync("KratosUiNodeInput", model)
}
</form>
</div>
}
</form>
}
</section>
</div>
</div>
</div>
9 changes: 9 additions & 0 deletions KratosSelfService/Views/Settings/Settings.cshtml.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
aside {
position: fixed;
z-index: 1;
}

i {
max-width: 16px;
max-height: 16px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

<div class="field">
<div class="is-fullwidth has-text-centered">
<a href="@attributes.Href" id="@attributes.Id">@attributes.Title.Text</a>
<a href="@attributes.Href" id="@attributes.Id">
@OryTranslator.ForUiText(attributes.Title)
</a>
</div>
@foreach (var message in Model.node.Messages)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
@{
var attributes = Model.node.Attributes.GetKratosUiNodeInputAttributes();
var uiText = attributes.Label ?? Model.node.Meta.Label;
var caption = OryTranslator.ForUiText(uiText);
}

<div class="field">
Expand All @@ -14,7 +13,7 @@
value="@attributes.Value" autocomplete="@attributes.Autocomplete"
disabled="@attributes.Disabled" name="@attributes.Name"
onclick="@attributes.Onclick" pattern="@attributes.Pattern">
@caption
@OryTranslator.ForUiText(uiText)
</button>
</div>
@foreach (var message in Model.node.Messages)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
var uiText = attributes.Label ?? Model.node.Meta.Label;
}

<div class="field">
<label class="checkbox">
<input type="checkbox" class="input"
required="@attributes.Required"
value="@attributes.Value" autocomplete="@attributes.Autocomplete.ToString()?.ToLower()"
disabled="@attributes.Disabled" name="@attributes.Name"
onclick="@attributes.Onclick" pattern="@attributes.Pattern"/>
@OryTranslator.ForUiText(uiText)
@foreach (var message in Model.node.Messages)
{
<KratosUiTextMessage Message="@message"/>
}
</div>
</label>
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
@{
var attributes = Model.node.Attributes.GetKratosUiNodeInputAttributes();
var uiText = attributes.Label ?? Model.node.Meta.Label;
var caption = OryTranslator.ForUiText(uiText);
}

<div class="field">
<label class="label">
@caption
@OryTranslator.ForUiText(uiText)
<div class="control">
<input type="@attributes.Type.ToString().ToLower()" class="input"
required="@attributes.Required"
Expand All @@ -19,7 +18,7 @@
</label>
@if (attributes.Type == KratosUiNodeInputAttributes.TypeEnum.Password
&& Model.node.Group == KratosUiNode.GroupEnum.Password
&& Model.loginFlow != null)
&& Model.FlowType == FlowType.Login)
{
<div class="field">
<div class="control">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@
@{
var attributes = Model.node.Attributes.GetKratosUiNodeInputAttributes();
var uiText = attributes.Label ?? Model.node.Meta.Label;
var caption = OryTranslator.ForUiText(uiText);
var isFullWidth = Model.FlowType != FlowType.Settings;
}

<div class="field">
<div class="control mt-5">
<button type="submit" class="button is-success is-fullwidth"
<button type="submit"
class="button is-success @(isFullWidth ? "is-fullwidth" : "")"
required="@attributes.Required"
disabled="@attributes.Disabled"
value="@attributes.Value"
name="@attributes.Name"
onclick="@attributes.Onclick">
@caption
@OryTranslator.ForUiText(uiText)
</button>
</div>
@foreach (var message in Model.node.Messages)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
}

<div class="field">
<p id="@attributes.Id">@attributes.Text.Text</p>
<p id="@attributes.Id">@OryTranslator.ForUiText(attributes.Text)</p>
@foreach (var message in Model.node.Messages)
{
<KratosUiTextMessage Message="@message"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@model KratosUiTextMessageModel

<div class="message @Model.CssClass p-2" id="@Model.UiText.Id">
<div class="message @Model.CssClass p-3" id="@Model.UiText.Id">
@Model.Content
</div>
Loading

0 comments on commit 297e2d4

Please sign in to comment.