Refactor AddEdit flow to not use hardcoded definitions (#1404)

This commit is contained in:
Leendert de Borst
2025-12-21 16:03:49 +01:00
parent 254901cfcf
commit c3cd81eb96
5 changed files with 492 additions and 455 deletions

View File

@@ -1,51 +0,0 @@
//-----------------------------------------------------------------------
// <copyright file="CustomFieldEdit.cs" company="aliasvault">
// Copyright (c) aliasvault. All rights reserved.
// Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information.
// </copyright>
//-----------------------------------------------------------------------
namespace AliasVault.Client.Main.Models;
using System;
/// <summary>
/// Model for custom field editing.
/// </summary>
public sealed class CustomFieldEdit
{
/// <summary>
/// Gets or sets the field value ID (for existing fields).
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the field definition ID.
/// </summary>
public Guid FieldDefinitionId { get; set; }
/// <summary>
/// Gets or sets the temporary ID for new custom fields (format: custom_{uuid}).
/// </summary>
public string? TempId { get; set; }
/// <summary>
/// Gets or sets the field label.
/// </summary>
public string Label { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the field type.
/// </summary>
public string FieldType { get; set; } = "Text";
/// <summary>
/// Gets or sets the field value.
/// </summary>
public string Value { get; set; } = string.Empty;
/// <summary>
/// Gets or sets a value indicating whether the field is hidden/masked.
/// </summary>
public bool IsHidden { get; set; }
}

View File

@@ -10,16 +10,15 @@ namespace AliasVault.Client.Main.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using AliasClientDb;
using AliasClientDb.Models;
using AliasVault.Client.Main.Models.FormValidation;
using AliasVault.Client.Resources;
using AliasVault.Client.Services;
/// <summary>
/// Item edit model for add/edit forms.
/// Uses a dynamic fields-based approach for flexibility when adding new system fields.
/// </summary>
public sealed class ItemEdit
{
@@ -40,11 +39,6 @@ public sealed class ItemEdit
[Display(Name = "Service Name")]
public string ServiceName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the URL of the service.
/// </summary>
public string? ServiceUrl { get; set; }
/// <summary>
/// Gets or sets the logo ID.
/// </summary>
@@ -60,77 +54,6 @@ public sealed class ItemEdit
/// </summary>
public Guid? FolderId { get; set; }
/// <summary>
/// Gets or sets the username field.
/// </summary>
public string Username { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the password field.
/// </summary>
public string Password { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the email field.
/// </summary>
public string Email { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the notes field.
/// </summary>
public string Notes { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the Alias first name.
/// </summary>
public string AliasFirstName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the Alias last name.
/// </summary>
public string AliasLastName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the Alias gender.
/// </summary>
public string AliasGender { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the Alias BirthDate. Can be empty string or a date in yyyy-MM-dd format.
/// </summary>
[StringDateFormat("yyyy-MM-dd", AllowEmpty = true)]
public string AliasBirthDate { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the credit card number.
/// </summary>
public string CardNumber { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the credit card cardholder name.
/// </summary>
public string CardCardholderName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the credit card expiry month.
/// </summary>
public string CardExpiryMonth { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the credit card expiry year.
/// </summary>
public string CardExpiryYear { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the credit card CVV.
/// </summary>
public string CardCvv { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the credit card PIN.
/// </summary>
public string CardPin { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the create date.
/// </summary>
@@ -141,6 +64,11 @@ public sealed class ItemEdit
/// </summary>
public DateTime LastUpdate { get; set; }
/// <summary>
/// Gets or sets the dynamic fields list (both system and custom fields).
/// </summary>
public List<SystemFieldEdit> Fields { get; set; } = [];
/// <summary>
/// Gets or sets the Attachment list.
/// </summary>
@@ -156,11 +84,6 @@ public sealed class ItemEdit
/// </summary>
public List<Passkey> Passkeys { get; set; } = [];
/// <summary>
/// Gets or sets the custom fields.
/// </summary>
public List<CustomFieldEdit> CustomFields { get; set; } = [];
/// <summary>
/// Creates an ItemEdit instance from an Item entity.
/// </summary>
@@ -168,31 +91,14 @@ public sealed class ItemEdit
/// <returns>A new ItemEdit instance.</returns>
public static ItemEdit FromEntity(Item item)
{
var birthDate = ItemService.GetFieldValue(item, FieldKey.AliasBirthdate);
var edit = new ItemEdit
{
Id = item.Id,
ItemType = item.ItemType,
ServiceName = item.Name ?? string.Empty,
ServiceUrl = ItemService.GetFieldValue(item, FieldKey.LoginUrl),
LogoId = item.LogoId,
ServiceLogo = item.Logo?.FileData,
FolderId = item.FolderId,
Username = ItemService.GetFieldValue(item, FieldKey.LoginUsername) ?? string.Empty,
Password = ItemService.GetFieldValue(item, FieldKey.LoginPassword) ?? string.Empty,
Email = ItemService.GetFieldValue(item, FieldKey.LoginEmail) ?? string.Empty,
Notes = ItemService.GetFieldValue(item, FieldKey.NotesContent) ?? string.Empty,
AliasFirstName = ItemService.GetFieldValue(item, FieldKey.AliasFirstName) ?? string.Empty,
AliasLastName = ItemService.GetFieldValue(item, FieldKey.AliasLastName) ?? string.Empty,
AliasGender = ItemService.GetFieldValue(item, FieldKey.AliasGender) ?? string.Empty,
AliasBirthDate = birthDate ?? string.Empty,
CardNumber = ItemService.GetFieldValue(item, FieldKey.CardNumber) ?? string.Empty,
CardCardholderName = ItemService.GetFieldValue(item, FieldKey.CardCardholderName) ?? string.Empty,
CardExpiryMonth = ItemService.GetFieldValue(item, FieldKey.CardExpiryMonth) ?? string.Empty,
CardExpiryYear = ItemService.GetFieldValue(item, FieldKey.CardExpiryYear) ?? string.Empty,
CardCvv = ItemService.GetFieldValue(item, FieldKey.CardCvv) ?? string.Empty,
CardPin = ItemService.GetFieldValue(item, FieldKey.CardPin) ?? string.Empty,
Attachments = item.Attachments.Where(a => !a.IsDeleted).ToList(),
TotpCodes = item.TotpCodes.Where(t => !t.IsDeleted).ToList(),
Passkeys = item.Passkeys.Where(p => !p.IsDeleted).ToList(),
@@ -200,17 +106,34 @@ public sealed class ItemEdit
LastUpdate = item.UpdatedAt,
};
// Extract custom fields (non-system fields that have FieldDefinitionId set)
foreach (var fv in item.FieldValues.Where(f => !f.IsDeleted && f.FieldDefinitionId != null))
// Convert all field values to SystemFieldEdit
foreach (var fv in item.FieldValues.Where(f => !f.IsDeleted))
{
edit.CustomFields.Add(new CustomFieldEdit
var isCustomField = fv.FieldDefinitionId != null && string.IsNullOrEmpty(fv.FieldKey);
var systemField = !string.IsNullOrEmpty(fv.FieldKey) ? SystemFieldRegistry.GetSystemField(fv.FieldKey) : null;
edit.Fields.Add(new SystemFieldEdit
{
Id = fv.Id,
FieldDefinitionId = fv.FieldDefinitionId!.Value,
Label = fv.FieldDefinition?.Label ?? "Custom Field",
FieldType = fv.FieldDefinition?.FieldType ?? "Text",
FieldKey = fv.FieldKey ?? fv.FieldDefinitionId?.ToString() ?? string.Empty,
FieldValueId = fv.Id,
FieldDefinitionId = fv.FieldDefinitionId,
Label = isCustomField
? fv.FieldDefinition?.Label ?? "Custom Field"
: fv.FieldKey ?? string.Empty,
FieldType = isCustomField
? fv.FieldDefinition?.FieldType ?? "Text"
: systemField?.FieldType ?? "Text",
Value = fv.Value ?? string.Empty,
IsHidden = fv.FieldDefinition?.IsHidden ?? false,
IsCustomField = isCustomField,
IsHidden = isCustomField
? fv.FieldDefinition?.IsHidden ?? false
: systemField?.IsHidden ?? false,
EnableHistory = isCustomField
? fv.FieldDefinition?.EnableHistory ?? false
: systemField?.EnableHistory ?? false,
DisplayOrder = systemField?.DefaultDisplayOrder ?? fv.Weight,
Category = GetCategoryFromFieldKey(fv.FieldKey),
IsMultiValue = systemField?.IsMultiValue ?? false,
});
}
@@ -234,152 +157,77 @@ public sealed class ItemEdit
TotpCodes = TotpCodes,
};
// Add login fields
if (!string.IsNullOrEmpty(ServiceUrl))
// Convert all fields to FieldValue entities
foreach (var field in Fields.Where(f => !string.IsNullOrEmpty(f.Value)))
{
ItemService.SetFieldValue(item, FieldKey.LoginUrl, ServiceUrl);
}
if (!string.IsNullOrEmpty(Username))
{
ItemService.SetFieldValue(item, FieldKey.LoginUsername, Username);
}
if (!string.IsNullOrEmpty(Password))
{
ItemService.SetFieldValue(item, FieldKey.LoginPassword, Password);
}
if (!string.IsNullOrEmpty(Email))
{
ItemService.SetFieldValue(item, FieldKey.LoginEmail, Email);
}
if (!string.IsNullOrEmpty(Notes))
{
ItemService.SetFieldValue(item, FieldKey.NotesContent, Notes);
}
// Add alias fields
if (!string.IsNullOrEmpty(AliasFirstName))
{
ItemService.SetFieldValue(item, FieldKey.AliasFirstName, AliasFirstName);
}
if (!string.IsNullOrEmpty(AliasLastName))
{
ItemService.SetFieldValue(item, FieldKey.AliasLastName, AliasLastName);
}
if (!string.IsNullOrEmpty(AliasGender))
{
ItemService.SetFieldValue(item, FieldKey.AliasGender, AliasGender);
}
if (!string.IsNullOrEmpty(AliasBirthDate))
{
ItemService.SetFieldValue(item, FieldKey.AliasBirthdate, AliasBirthDate);
}
// Add card fields
if (!string.IsNullOrEmpty(CardNumber))
{
ItemService.SetFieldValue(item, FieldKey.CardNumber, CardNumber);
}
if (!string.IsNullOrEmpty(CardCardholderName))
{
ItemService.SetFieldValue(item, FieldKey.CardCardholderName, CardCardholderName);
}
if (!string.IsNullOrEmpty(CardExpiryMonth))
{
ItemService.SetFieldValue(item, FieldKey.CardExpiryMonth, CardExpiryMonth);
}
if (!string.IsNullOrEmpty(CardExpiryYear))
{
ItemService.SetFieldValue(item, FieldKey.CardExpiryYear, CardExpiryYear);
}
if (!string.IsNullOrEmpty(CardCvv))
{
ItemService.SetFieldValue(item, FieldKey.CardCvv, CardCvv);
}
if (!string.IsNullOrEmpty(CardPin))
{
ItemService.SetFieldValue(item, FieldKey.CardPin, CardPin);
}
// Add custom fields
foreach (var customField in CustomFields.Where(cf => !string.IsNullOrEmpty(cf.Value)))
{
// For new custom fields (TempId set, FieldDefinitionId is empty), create a new FieldDefinition.
// The FieldDefinition.Id is a plain GUID - no prefix needed.
// Custom fields are identified by having FieldDefinitionId set (not FieldKey).
if (customField.FieldDefinitionId == Guid.Empty && !string.IsNullOrEmpty(customField.TempId))
if (field.IsCustomField)
{
// Custom field handling
var now = DateTime.UtcNow;
// TempId is a plain GUID string
var fieldDefinitionId = Guid.TryParse(customField.TempId, out var parsedGuid)
? parsedGuid
: Guid.NewGuid();
if (field.FieldDefinitionId == null || field.FieldDefinitionId == Guid.Empty)
{
// New custom field - create FieldDefinition
var fieldDefinitionId = !string.IsNullOrEmpty(field.TempId) && Guid.TryParse(field.TempId, out var parsedGuid)
? parsedGuid
: Guid.NewGuid();
var fieldDefinition = new FieldDefinition
{
Id = fieldDefinitionId,
FieldType = customField.FieldType,
Label = customField.Label,
IsHidden = customField.IsHidden,
IsMultiValue = false,
EnableHistory = false,
Weight = 0,
CreatedAt = now,
UpdatedAt = now,
};
var fieldDefinition = new FieldDefinition
{
Id = fieldDefinitionId,
FieldType = field.FieldType,
Label = field.Label,
IsHidden = field.IsHidden,
IsMultiValue = false,
EnableHistory = false,
Weight = 0,
CreatedAt = now,
UpdatedAt = now,
};
item.FieldValues.Add(new FieldValue
item.FieldValues.Add(new FieldValue
{
Id = Guid.NewGuid(),
ItemId = item.Id,
FieldDefinitionId = fieldDefinitionId,
FieldDefinition = fieldDefinition,
FieldKey = null,
Value = field.Value,
Weight = 0,
});
}
else
{
Id = Guid.NewGuid(),
ItemId = item.Id,
FieldDefinitionId = fieldDefinitionId,
FieldDefinition = fieldDefinition,
FieldKey = null,
Value = customField.Value,
Weight = 0,
});
}
else
{
// Existing custom field - update value and include FieldDefinition with updated label
var fieldValue = new FieldValue
{
Id = customField.Id != Guid.Empty ? customField.Id : Guid.NewGuid(),
ItemId = item.Id,
FieldDefinitionId = customField.FieldDefinitionId,
FieldKey = null,
Value = customField.Value,
Weight = 0,
};
// Existing custom field - update value and include FieldDefinition
var fieldValue = new FieldValue
{
Id = field.FieldValueId != Guid.Empty ? field.FieldValueId : Guid.NewGuid(),
ItemId = item.Id,
FieldDefinitionId = field.FieldDefinitionId,
FieldKey = null,
Value = field.Value,
Weight = 0,
};
// Include FieldDefinition with potentially updated label for update logic
if (customField.FieldDefinitionId != Guid.Empty)
{
// Include FieldDefinition with potentially updated label
fieldValue.FieldDefinition = new FieldDefinition
{
Id = customField.FieldDefinitionId,
Label = customField.Label,
FieldType = customField.FieldType,
IsHidden = customField.IsHidden,
Id = field.FieldDefinitionId.Value,
Label = field.Label,
FieldType = field.FieldType,
IsHidden = field.IsHidden,
IsMultiValue = false,
EnableHistory = false,
Weight = 0,
};
}
item.FieldValues.Add(fieldValue);
item.FieldValues.Add(fieldValue);
}
}
else
{
// System field
ItemService.SetFieldValue(item, field.FieldKey, field.Value);
}
}
@@ -387,87 +235,86 @@ public sealed class ItemEdit
}
/// <summary>
/// Gets the value of a system field by its field key.
/// Gets the value of a field by its field key.
/// </summary>
/// <param name="fieldKey">The field key.</param>
/// <returns>The field value or empty string.</returns>
public string GetFieldValue(string fieldKey)
{
return fieldKey switch
{
FieldKey.LoginUrl => ServiceUrl ?? string.Empty,
FieldKey.LoginUsername => Username,
FieldKey.LoginPassword => Password,
FieldKey.LoginEmail => Email,
FieldKey.NotesContent => Notes,
FieldKey.AliasFirstName => AliasFirstName,
FieldKey.AliasLastName => AliasLastName,
FieldKey.AliasGender => AliasGender,
FieldKey.AliasBirthdate => AliasBirthDate,
FieldKey.CardNumber => CardNumber,
FieldKey.CardCardholderName => CardCardholderName,
FieldKey.CardExpiryMonth => CardExpiryMonth,
FieldKey.CardExpiryYear => CardExpiryYear,
FieldKey.CardCvv => CardCvv,
FieldKey.CardPin => CardPin,
_ => string.Empty,
};
return Fields.FirstOrDefault(f => f.FieldKey == fieldKey)?.Value ?? string.Empty;
}
/// <summary>
/// Sets the value of a system field by its field key.
/// Gets a field by its field key.
/// If the field doesn't exist, creates it based on the system field definition.
/// </summary>
/// <param name="fieldKey">The field key.</param>
/// <returns>The field or null if not a valid system field.</returns>
public SystemFieldEdit? GetField(string fieldKey)
{
var field = Fields.FirstOrDefault(f => f.FieldKey == fieldKey);
if (field != null)
{
return field;
}
// Field doesn't exist - create it if it's a valid system field
var systemField = SystemFieldRegistry.GetSystemField(fieldKey);
if (systemField == null)
{
return null;
}
field = new SystemFieldEdit
{
FieldKey = fieldKey,
Label = fieldKey,
FieldType = systemField.FieldType,
Value = string.Empty,
IsCustomField = false,
IsHidden = systemField.IsHidden,
EnableHistory = systemField.EnableHistory,
DisplayOrder = systemField.DefaultDisplayOrder,
Category = systemField.Category,
IsMultiValue = systemField.IsMultiValue,
};
Fields.Add(field);
return field;
}
/// <summary>
/// Sets the value of a field by its field key.
/// If the field doesn't exist, it will be added.
/// </summary>
/// <param name="fieldKey">The field key.</param>
/// <param name="value">The value to set.</param>
public void SetFieldValue(string fieldKey, string value)
{
switch (fieldKey)
var field = Fields.FirstOrDefault(f => f.FieldKey == fieldKey);
if (field != null)
{
case FieldKey.LoginUrl:
ServiceUrl = value;
break;
case FieldKey.LoginUsername:
Username = value;
break;
case FieldKey.LoginPassword:
Password = value;
break;
case FieldKey.LoginEmail:
Email = value;
break;
case FieldKey.NotesContent:
Notes = value;
break;
case FieldKey.AliasFirstName:
AliasFirstName = value;
break;
case FieldKey.AliasLastName:
AliasLastName = value;
break;
case FieldKey.AliasGender:
AliasGender = value;
break;
case FieldKey.AliasBirthdate:
AliasBirthDate = value;
break;
case FieldKey.CardNumber:
CardNumber = value;
break;
case FieldKey.CardCardholderName:
CardCardholderName = value;
break;
case FieldKey.CardExpiryMonth:
CardExpiryMonth = value;
break;
case FieldKey.CardExpiryYear:
CardExpiryYear = value;
break;
case FieldKey.CardCvv:
CardCvv = value;
break;
case FieldKey.CardPin:
CardPin = value;
break;
field.Value = value;
}
else
{
// Add new field based on system field definition
var systemField = SystemFieldRegistry.GetSystemField(fieldKey);
if (systemField != null)
{
Fields.Add(new SystemFieldEdit
{
FieldKey = fieldKey,
Label = fieldKey,
FieldType = systemField.FieldType,
Value = value,
IsCustomField = false,
IsHidden = systemField.IsHidden,
EnableHistory = systemField.EnableHistory,
DisplayOrder = systemField.DefaultDisplayOrder,
Category = systemField.Category,
IsMultiValue = systemField.IsMultiValue,
});
}
}
}
@@ -480,4 +327,167 @@ public sealed class ItemEdit
{
return !string.IsNullOrEmpty(GetFieldValue(fieldKey));
}
/// <summary>
/// Gets all custom fields.
/// </summary>
/// <returns>List of custom fields.</returns>
public List<SystemFieldEdit> GetCustomFields()
{
return Fields.Where(f => f.IsCustomField).ToList();
}
/// <summary>
/// Gets all system fields.
/// </summary>
/// <returns>List of system fields.</returns>
public List<SystemFieldEdit> GetSystemFields()
{
return Fields.Where(f => !f.IsCustomField).ToList();
}
/// <summary>
/// Adds a new custom field.
/// </summary>
/// <param name="label">The field label.</param>
/// <param name="fieldType">The field type.</param>
public void AddCustomField(string label, string fieldType)
{
var tempId = Guid.NewGuid().ToString();
Fields.Add(new SystemFieldEdit
{
FieldKey = tempId,
TempId = tempId,
Label = label,
FieldType = fieldType,
Value = string.Empty,
IsCustomField = true,
IsHidden = fieldType == "Hidden" || fieldType == "Password",
EnableHistory = false,
DisplayOrder = Fields.Count,
Category = FieldCategory.Custom,
IsMultiValue = false,
});
}
/// <summary>
/// Removes a custom field by its field key (TempId or FieldDefinitionId).
/// </summary>
/// <param name="fieldKey">The field key to remove.</param>
public void RemoveCustomField(string fieldKey)
{
var field = Fields.FirstOrDefault(f => f.FieldKey == fieldKey && f.IsCustomField);
if (field != null)
{
Fields.Remove(field);
}
}
/// <summary>
/// Updates the label of a custom field.
/// </summary>
/// <param name="fieldKey">The field key.</param>
/// <param name="newLabel">The new label.</param>
public void UpdateCustomFieldLabel(string fieldKey, string newLabel)
{
var field = Fields.FirstOrDefault(f => f.FieldKey == fieldKey && f.IsCustomField);
if (field != null)
{
field.Label = newLabel;
}
}
/// <summary>
/// Removes a field by its field key and clears its value.
/// </summary>
/// <param name="fieldKey">The field key to remove.</param>
public void RemoveField(string fieldKey)
{
var field = Fields.FirstOrDefault(f => f.FieldKey == fieldKey);
if (field != null)
{
if (field.IsCustomField)
{
Fields.Remove(field);
}
else
{
// For system fields, just clear the value (they can be re-added)
field.Value = string.Empty;
}
}
}
/// <summary>
/// Gets fields by category.
/// </summary>
/// <param name="category">The category to filter by.</param>
/// <returns>List of fields in the specified category.</returns>
public List<SystemFieldEdit> GetFieldsByCategory(FieldCategory category)
{
return Fields
.Where(f => f.Category == category && !string.IsNullOrEmpty(f.Value))
.OrderBy(f => f.DisplayOrder)
.ToList();
}
/// <summary>
/// Clears all field values for a specific item type that don't apply to the new type.
/// </summary>
/// <param name="newItemType">The new item type.</param>
public void ClearFieldsNotApplicableToType(string newItemType)
{
foreach (var field in Fields.Where(f => !f.IsCustomField).ToList())
{
var systemField = SystemFieldRegistry.GetSystemField(field.FieldKey);
if (systemField != null && !SystemFieldRegistry.FieldAppliesToType(systemField, newItemType))
{
field.Value = string.Empty;
}
}
}
/// <summary>
/// Gets the field category from a field key based on its prefix.
/// </summary>
private static FieldCategory GetCategoryFromFieldKey(string? fieldKey)
{
if (string.IsNullOrEmpty(fieldKey))
{
return FieldCategory.Custom;
}
if (fieldKey.StartsWith("login."))
{
// URL is in Primary category
if (fieldKey == FieldKey.LoginUrl)
{
return FieldCategory.Primary;
}
return FieldCategory.Login;
}
if (fieldKey.StartsWith("alias."))
{
return FieldCategory.Alias;
}
if (fieldKey.StartsWith("card."))
{
return FieldCategory.Card;
}
if (fieldKey.StartsWith("notes."))
{
return FieldCategory.Notes;
}
if (fieldKey.StartsWith("metadata."))
{
return FieldCategory.Metadata;
}
return FieldCategory.Custom;
}
}

View File

@@ -0,0 +1,87 @@
//-----------------------------------------------------------------------
// <copyright file="SystemFieldEdit.cs" company="aliasvault">
// Copyright (c) aliasvault. All rights reserved.
// Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information.
// </copyright>
//-----------------------------------------------------------------------
namespace AliasVault.Client.Main.Models;
using System;
using AliasClientDb.Models;
/// <summary>
/// Represents a field for editing in the UI.
/// Unifies both system fields (from SystemFieldRegistry) and custom fields for the edit form.
/// </summary>
public sealed class SystemFieldEdit
{
/// <summary>
/// Gets or sets the field key.
/// For system fields: the system field key (e.g., 'login.username').
/// For custom fields: the FieldDefinitionId as a string.
/// </summary>
public string FieldKey { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the field value ID (for existing fields only).
/// </summary>
public Guid FieldValueId { get; set; }
/// <summary>
/// Gets or sets the field definition ID (for custom fields only).
/// </summary>
public Guid? FieldDefinitionId { get; set; }
/// <summary>
/// Gets or sets the temporary ID for new custom fields (a GUID string).
/// </summary>
public string? TempId { get; set; }
/// <summary>
/// Gets or sets the label for this field.
/// For system fields, this is the field key (UI layer translates via fieldLabels.*).
/// For custom fields, this is the user-defined label.
/// </summary>
public string Label { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the field type for rendering (Text, Password, Email, URL, Date, etc.).
/// </summary>
public string FieldType { get; set; } = "Text";
/// <summary>
/// Gets or sets the field value.
/// </summary>
public string Value { get; set; } = string.Empty;
/// <summary>
/// Gets or sets a value indicating whether this is a custom field.
/// </summary>
public bool IsCustomField { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the field is hidden/masked.
/// </summary>
public bool IsHidden { get; set; }
/// <summary>
/// Gets or sets a value indicating whether history is enabled for this field.
/// </summary>
public bool EnableHistory { get; set; }
/// <summary>
/// Gets or sets the display order.
/// </summary>
public int DisplayOrder { get; set; }
/// <summary>
/// Gets or sets the field category.
/// </summary>
public FieldCategory Category { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the field supports multiple values.
/// </summary>
public bool IsMultiValue { get; set; }
}

View File

@@ -56,7 +56,7 @@ else
@if (ShouldShowField(FieldKey.LoginUrl))
{
<div class="col-span-6 sm:col-span-3">
<EditFormRow Id="service-url" OnFocus="OnFocusUrlInput" Label="@Localizer["ServiceUrlLabel"]" @bind-Value="Obj.ServiceUrl"></EditFormRow>
<EditFormRow Id="service-url" OnFocus="OnFocusUrlInput" Label="@Localizer["ServiceUrlLabel"]" @bind-Value="Obj.GetField(FieldKey.LoginUrl)!.Value"></EditFormRow>
</div>
}
</div>
@@ -76,7 +76,7 @@ else
<div class="col-span-1 md:col-span-1 lg:col-span-1">
<RemovableSection CanRemove="@CanRemoveField(FieldKey.NotesContent)" OnRemove="() => RemoveOptionalField(FieldKey.NotesContent)">
<div class="col-span-6 sm:col-span-3">
<EditFormRow Type="textarea" Id="notes" Label="@Localizer["NotesLabel"]" LabelStyle="EditFormRow.FormLabelStyle.Header" @bind-Value="Obj.Notes"></EditFormRow>
<EditFormRow Type="textarea" Id="notes" Label="@Localizer["NotesLabel"]" LabelStyle="EditFormRow.FormLabelStyle.Header" @bind-Value="Obj.GetField(FieldKey.NotesContent)!.Value"></EditFormRow>
</div>
</RemovableSection>
</div>
@@ -109,7 +109,7 @@ else
var passkey = Obj.Passkeys.First();
@* With passkey: Username, Passkey, Email, Password *@
<div class="col-span-6">
<EditUsernameFormRow Id="username" Label="@Localizer["UsernameLabel"]" @bind-Value="Obj.Username" OnGenerateNewUsername="GenerateRandomUsername"></EditUsernameFormRow>
<EditUsernameFormRow Id="username" Label="@Localizer["UsernameLabel"]" @bind-Value="Obj.GetField(FieldKey.LoginUsername)!.Value" OnGenerateNewUsername="GenerateRandomUsername"></EditUsernameFormRow>
</div>
@if (!PasskeyMarkedForDeletion)
{
@@ -182,10 +182,10 @@ else
</div>
}
<div class="col-span-6">
<EditEmailFormRow Id="email" Label="@Localizer["EmailLabel"]" @bind-Value="Obj.Email"></EditEmailFormRow>
<EditEmailFormRow Id="email" Label="@Localizer["EmailLabel"]" @bind-Value="Obj.GetField(FieldKey.LoginEmail)!.Value"></EditEmailFormRow>
</div>
<div class="col-span-6">
<EditPasswordFormRow Id="password" Label="@Localizer["PasswordLabel"]" @bind-Value="Obj.Password" ShowPassword="IsPasswordVisible"></EditPasswordFormRow>
<EditPasswordFormRow Id="password" Label="@Localizer["PasswordLabel"]" @bind-Value="Obj.GetField(FieldKey.LoginPassword)!.Value" ShowPassword="IsPasswordVisible"></EditPasswordFormRow>
</div>
}
else
@@ -194,7 +194,7 @@ else
@if (ShouldShowField(FieldKey.LoginEmail))
{
<div class="col-span-6 relative">
<EditEmailFormRow Id="email" Label="@Localizer["EmailLabel"]" @bind-Value="Obj.Email"></EditEmailFormRow>
<EditEmailFormRow Id="email" Label="@Localizer["EmailLabel"]" @bind-Value="Obj.GetField(FieldKey.LoginEmail)!.Value"></EditEmailFormRow>
@if (CanRemoveField(FieldKey.LoginEmail))
{
<button type="button" @onclick="() => RemoveOptionalField(FieldKey.LoginEmail)" @onclick:preventDefault="true"
@@ -221,10 +221,10 @@ else
}
<div class="col-span-6">
<EditUsernameFormRow Id="username" Label="@Localizer["UsernameLabel"]" @bind-Value="Obj.Username" OnGenerateNewUsername="GenerateRandomUsername"></EditUsernameFormRow>
<EditUsernameFormRow Id="username" Label="@Localizer["UsernameLabel"]" @bind-Value="Obj.GetField(FieldKey.LoginUsername)!.Value" OnGenerateNewUsername="GenerateRandomUsername"></EditUsernameFormRow>
</div>
<div class="col-span-6">
<EditPasswordFormRow Id="password" Label="@Localizer["PasswordLabel"]" @bind-Value="Obj.Password" ShowPassword="IsPasswordVisible"></EditPasswordFormRow>
<EditPasswordFormRow Id="password" Label="@Localizer["PasswordLabel"]" @bind-Value="Obj.GetField(FieldKey.LoginPassword)!.Value" ShowPassword="IsPasswordVisible"></EditPasswordFormRow>
</div>
}
</div>
@@ -259,17 +259,16 @@ else
</div>
<div class="grid gap-6">
<div class="col-span-6 sm:col-span-3">
<EditFormRow Id="first-name" Label="@Localizer["FirstNameLabel"]" @bind-Value="Obj.AliasFirstName"></EditFormRow>
<EditFormRow Id="first-name" Label="@Localizer["FirstNameLabel"]" @bind-Value="Obj.GetField(FieldKey.AliasFirstName)!.Value"></EditFormRow>
</div>
<div class="col-span-6 sm:col-span-3">
<EditFormRow Id="last-name" Label="@Localizer["LastNameLabel"]" @bind-Value="Obj.AliasLastName"></EditFormRow>
<EditFormRow Id="last-name" Label="@Localizer["LastNameLabel"]" @bind-Value="Obj.GetField(FieldKey.AliasLastName)!.Value"></EditFormRow>
</div>
<div class="col-span-6 sm:col-span-3">
<EditFormRow Id="gender" Label="@Localizer["GenderLabel"]" @bind-Value="Obj.AliasGender"></EditFormRow>
<EditFormRow Id="gender" Label="@Localizer["GenderLabel"]" @bind-Value="Obj.GetField(FieldKey.AliasGender)!.Value"></EditFormRow>
</div>
<div class="col-span-6 sm:col-span-3">
<EditFormRow Id="birthdate" Label="@Localizer["BirthDateLabel"]" @bind-Value="Obj.AliasBirthDate"></EditFormRow>
<ValidationMessage For="() => Obj.AliasBirthDate"/>
<EditFormRow Id="birthdate" Label="@Localizer["BirthDateLabel"]" @bind-Value="Obj.GetField(FieldKey.AliasBirthdate)!.Value"></EditFormRow>
</div>
</div>
</div>
@@ -284,24 +283,24 @@ else
<h3 class="mb-4 text-xl font-semibold dark:text-white">@Localizer["CardDetailsSectionHeader"]</h3>
<div class="grid gap-6">
<div class="col-span-6">
<EditFormRow Id="cardholder-name" Label="@Localizer["CardholderNameLabel"]" @bind-Value="Obj.CardCardholderName"></EditFormRow>
<EditFormRow Id="cardholder-name" Label="@Localizer["CardholderNameLabel"]" @bind-Value="Obj.GetField(FieldKey.CardCardholderName)!.Value"></EditFormRow>
</div>
<div class="col-span-6">
<EditPasswordFormRow Id="card-number" Label="@Localizer["CardNumberLabel"]" @bind-Value="Obj.CardNumber" ShowPassword="false" ShowGenerateButtons="false"></EditPasswordFormRow>
<EditPasswordFormRow Id="card-number" Label="@Localizer["CardNumberLabel"]" @bind-Value="Obj.GetField(FieldKey.CardNumber)!.Value" ShowPassword="false" ShowGenerateButtons="false"></EditPasswordFormRow>
</div>
<div class="col-span-3">
<EditFormRow Id="expiry-month" Label="@Localizer["ExpiryMonthLabel"]" Placeholder="MM" @bind-Value="Obj.CardExpiryMonth"></EditFormRow>
<EditFormRow Id="expiry-month" Label="@Localizer["ExpiryMonthLabel"]" Placeholder="MM" @bind-Value="Obj.GetField(FieldKey.CardExpiryMonth)!.Value"></EditFormRow>
</div>
<div class="col-span-3">
<EditFormRow Id="expiry-year" Label="@Localizer["ExpiryYearLabel"]" Placeholder="YYYY" @bind-Value="Obj.CardExpiryYear"></EditFormRow>
<EditFormRow Id="expiry-year" Label="@Localizer["ExpiryYearLabel"]" Placeholder="YYYY" @bind-Value="Obj.GetField(FieldKey.CardExpiryYear)!.Value"></EditFormRow>
</div>
<div class="col-span-6 sm:col-span-3">
<EditPasswordFormRow Id="card-cvv" Label="@Localizer["CardCvvLabel"]" @bind-Value="Obj.CardCvv" ShowPassword="false" ShowGenerateButtons="false"></EditPasswordFormRow>
<EditPasswordFormRow Id="card-cvv" Label="@Localizer["CardCvvLabel"]" @bind-Value="Obj.GetField(FieldKey.CardCvv)!.Value" ShowPassword="false" ShowGenerateButtons="false"></EditPasswordFormRow>
</div>
@if (ShouldShowField(FieldKey.CardPin))
{
<div class="col-span-6 sm:col-span-3 relative">
<EditPasswordFormRow Id="card-pin" Label="@Localizer["CardPinLabel"]" @bind-Value="Obj.CardPin" ShowPassword="false"></EditPasswordFormRow>
<EditPasswordFormRow Id="card-pin" Label="@Localizer["CardPinLabel"]" @bind-Value="Obj.GetField(FieldKey.CardPin)!.Value" ShowPassword="false"></EditPasswordFormRow>
@if (CanRemoveField(FieldKey.CardPin))
{
<button type="button" @onclick="() => RemoveOptionalField(FieldKey.CardPin)" @onclick:preventDefault="true"
@@ -319,34 +318,37 @@ else
}
@* Custom Fields Section *@
@if (Obj.CustomFields.Any())
@{
var customFields = Obj.GetCustomFields();
}
@if (customFields.Any())
{
<div class="col-span-1 md:col-span-1 lg:col-span-2">
<div class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm 2xl:col-span-2 dark:border-gray-700 sm:p-6 dark:bg-gray-800">
<h3 class="mb-4 text-xl font-semibold dark:text-white">@Localizer["CustomFieldsSectionHeader"]</h3>
<div class="grid gap-6">
@foreach (var customField in Obj.CustomFields)
@foreach (var customField in customFields)
{
<div class="col-span-6">
<EditableFieldLabel
HtmlFor="@($"custom-{customField.TempId ?? customField.Id.ToString()}")"
HtmlFor="@($"custom-{customField.TempId ?? customField.FieldKey}")"
Label="@customField.Label"
LabelChanged="@((newLabel) => UpdateCustomFieldLabel(customField, newLabel))"
OnDelete="@(() => RemoveCustomField(customField))" />
LabelChanged="@((newLabel) => Obj.UpdateCustomFieldLabel(customField.FieldKey, newLabel))"
OnDelete="@(() => Obj.RemoveCustomField(customField.FieldKey))" />
@if (customField.FieldType == "TextArea")
{
<div class="relative">
<textarea id="@($"custom-{customField.TempId ?? customField.Id.ToString()}")" style="height: 200px;" class="outline-0 shadow-sm bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white" @bind="customField.Value"></textarea>
<textarea id="@($"custom-{customField.TempId ?? customField.FieldKey}")" style="height: 200px;" class="outline-0 shadow-sm bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white" @bind="customField.Value"></textarea>
</div>
}
else if (customField.IsHidden || customField.FieldType == "Hidden" || customField.FieldType == "Password")
{
<EditPasswordFormRow Id="@($"custom-{customField.TempId ?? customField.Id.ToString()}")" Label="" @bind-Value="customField.Value" ShowPassword="false"></EditPasswordFormRow>
<EditPasswordFormRow Id="@($"custom-{customField.TempId ?? customField.FieldKey}")" Label="" @bind-Value="customField.Value" ShowPassword="false"></EditPasswordFormRow>
}
else
{
<div class="relative">
<input type="text" id="@($"custom-{customField.TempId ?? customField.Id.ToString()}")" autocomplete="off" class="outline-0 shadow-sm bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white" @bind="customField.Value" autocapitalize="off" autocorrect="off">
<input type="text" id="@($"custom-{customField.TempId ?? customField.FieldKey}")" autocomplete="off" class="outline-0 shadow-sm bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white" @bind="customField.Value" autocapitalize="off" autocorrect="off">
</div>
}
</div>
@@ -475,7 +477,7 @@ else
}
if (!string.IsNullOrEmpty(QuickCreateStateService.ServiceUrl))
{
Obj.ServiceUrl = QuickCreateStateService.ServiceUrl;
Obj.SetFieldValue(FieldKey.LoginUrl, QuickCreateStateService.ServiceUrl);
}
// Clear the state after using it
@@ -728,35 +730,7 @@ else
/// </summary>
private void AddCustomField((string Label, string FieldType) args)
{
// Use a plain GUID for the TempId - no prefix needed.
// Custom fields are identified by having FieldDefinitionId set (not FieldKey).
var tempId = Guid.NewGuid().ToString();
Obj.CustomFields.Add(new CustomFieldEdit
{
TempId = tempId,
Label = args.Label,
FieldType = args.FieldType,
IsHidden = args.FieldType == "Hidden" || args.FieldType == "Password",
Value = string.Empty
});
StateHasChanged();
}
/// <summary>
/// Removes a custom field.
/// </summary>
private void RemoveCustomField(CustomFieldEdit field)
{
Obj.CustomFields.Remove(field);
StateHasChanged();
}
/// <summary>
/// Updates the label of a custom field.
/// </summary>
private void UpdateCustomFieldLabel(CustomFieldEdit field, string newLabel)
{
field.Label = newLabel;
Obj.AddCustomField(args.Label, args.FieldType);
StateHasChanged();
}
@@ -849,9 +823,10 @@ else
Show2FA = Obj.TotpCodes.Any();
ShowAttachments = Obj.Attachments.Any();
if (Obj.ServiceUrl is null)
// Set default URL if not set
if (!Obj.HasFieldValue(FieldKey.LoginUrl))
{
Obj.ServiceUrl = ItemService.DefaultServiceUrl;
Obj.SetFieldValue(FieldKey.LoginUrl, ItemService.DefaultServiceUrl);
}
}
@@ -881,8 +856,8 @@ else
{
Obj = ItemEdit.FromEntity(CreateNewItemObject());
Obj.AliasBirthDate = string.Empty;
Obj.ServiceUrl = ItemService.DefaultServiceUrl;
Obj.SetFieldValue(FieldKey.AliasBirthdate, string.Empty);
Obj.SetFieldValue(FieldKey.LoginUrl, ItemService.DefaultServiceUrl);
}
/// <summary>
@@ -899,7 +874,8 @@ else
/// </summary>
private void OnFocusUrlInput(FocusEventArgs e)
{
if (Obj.ServiceUrl != ItemService.DefaultServiceUrl)
var currentUrl = Obj.GetFieldValue(FieldKey.LoginUrl);
if (currentUrl != ItemService.DefaultServiceUrl)
{
return;
}
@@ -936,20 +912,20 @@ else
private void ClearAliasFields()
{
Obj.AliasFirstName = string.Empty;
Obj.AliasLastName = string.Empty;
Obj.AliasGender = string.Empty;
Obj.AliasBirthDate = string.Empty;
Obj.SetFieldValue(FieldKey.AliasFirstName, string.Empty);
Obj.SetFieldValue(FieldKey.AliasLastName, string.Empty);
Obj.SetFieldValue(FieldKey.AliasGender, string.Empty);
Obj.SetFieldValue(FieldKey.AliasBirthdate, string.Empty);
StateHasChanged();
}
private bool HasAliasValues()
{
return !string.IsNullOrWhiteSpace(Obj.AliasFirstName) ||
!string.IsNullOrWhiteSpace(Obj.AliasLastName) ||
!string.IsNullOrWhiteSpace(Obj.AliasGender) ||
!string.IsNullOrWhiteSpace(Obj.AliasBirthDate);
return !string.IsNullOrWhiteSpace(Obj.GetFieldValue(FieldKey.AliasFirstName)) ||
!string.IsNullOrWhiteSpace(Obj.GetFieldValue(FieldKey.AliasLastName)) ||
!string.IsNullOrWhiteSpace(Obj.GetFieldValue(FieldKey.AliasGender)) ||
!string.IsNullOrWhiteSpace(Obj.GetFieldValue(FieldKey.AliasBirthdate));
}
private string GetToggleButtonClasses()
@@ -964,39 +940,45 @@ else
private async Task GenerateRandomAlias()
{
string currentUsername = Obj.Username ?? string.Empty;
string currentPassword = Obj.Password ?? string.Empty;
string currentEmail = Obj.Email ?? string.Empty;
string currentUsername = Obj.GetFieldValue(FieldKey.LoginUsername);
string currentPassword = Obj.GetFieldValue(FieldKey.LoginPassword);
string currentEmail = Obj.GetFieldValue(FieldKey.LoginEmail);
var generatedItem = await ItemService.GenerateRandomIdentityAsync(Obj.ToEntity());
var generatedObj = ItemEdit.FromEntity(generatedItem);
Obj.Username = currentUsername;
Obj.Password = currentPassword;
Obj.Email = currentEmail;
// Keep the current values if user has modified them
Obj.SetFieldValue(FieldKey.LoginUsername, currentUsername);
Obj.SetFieldValue(FieldKey.LoginPassword, currentPassword);
Obj.SetFieldValue(FieldKey.LoginEmail, currentEmail);
Obj.AliasFirstName = generatedObj.AliasFirstName;
Obj.AliasLastName = generatedObj.AliasLastName;
Obj.AliasGender = generatedObj.AliasGender;
Obj.AliasBirthDate = generatedObj.AliasBirthDate;
// Always update alias fields
Obj.SetFieldValue(FieldKey.AliasFirstName, generatedObj.GetFieldValue(FieldKey.AliasFirstName));
Obj.SetFieldValue(FieldKey.AliasLastName, generatedObj.GetFieldValue(FieldKey.AliasLastName));
Obj.SetFieldValue(FieldKey.AliasGender, generatedObj.GetFieldValue(FieldKey.AliasGender));
Obj.SetFieldValue(FieldKey.AliasBirthdate, generatedObj.GetFieldValue(FieldKey.AliasBirthdate));
var generatedUsername = generatedObj.GetFieldValue(FieldKey.LoginUsername);
var generatedPassword = generatedObj.GetFieldValue(FieldKey.LoginPassword);
var generatedEmail = generatedObj.GetFieldValue(FieldKey.LoginEmail);
if (string.IsNullOrWhiteSpace(currentUsername) || currentUsername == LastGeneratedUsername)
{
Obj.Username = generatedObj.Username;
LastGeneratedUsername = generatedObj.Username;
Obj.SetFieldValue(FieldKey.LoginUsername, generatedUsername);
LastGeneratedUsername = generatedUsername;
}
if (string.IsNullOrWhiteSpace(currentPassword) || currentPassword == LastGeneratedPassword)
{
Obj.Password = generatedObj.Password;
LastGeneratedPassword = generatedObj.Password;
Obj.SetFieldValue(FieldKey.LoginPassword, generatedPassword);
LastGeneratedPassword = generatedPassword;
IsPasswordVisible = true;
}
if (string.IsNullOrWhiteSpace(currentEmail) || currentEmail == LastGeneratedEmail || currentEmail.StartsWith("@"))
{
Obj.Email = generatedObj.Email;
LastGeneratedEmail = generatedObj.Email;
Obj.SetFieldValue(FieldKey.LoginEmail, generatedEmail);
LastGeneratedEmail = generatedEmail;
}
StateHasChanged();
@@ -1007,34 +989,38 @@ else
/// </summary>
private async Task GenerateRandomUsername()
{
var aliasFirstName = Obj.GetFieldValue(FieldKey.AliasFirstName);
var aliasLastName = Obj.GetFieldValue(FieldKey.AliasLastName);
var aliasBirthDate = Obj.GetFieldValue(FieldKey.AliasBirthdate);
AliasVaultIdentity identity;
if (string.IsNullOrWhiteSpace(Obj.AliasFirstName) && string.IsNullOrWhiteSpace(Obj.AliasLastName) && string.IsNullOrWhiteSpace(Obj.AliasBirthDate))
if (string.IsNullOrWhiteSpace(aliasFirstName) && string.IsNullOrWhiteSpace(aliasLastName) && string.IsNullOrWhiteSpace(aliasBirthDate))
{
var randomIdentity = await ItemService.GenerateRandomIdentityAsync(CreateNewItemObject());
var randomIdentityEdit = ItemEdit.FromEntity(randomIdentity);
identity = new AliasVaultIdentity
{
FirstName = randomIdentityEdit.AliasFirstName ?? string.Empty,
LastName = randomIdentityEdit.AliasLastName ?? string.Empty,
BirthDate = randomIdentityEdit.AliasBirthDate ?? string.Empty,
Gender = randomIdentityEdit.AliasGender ?? string.Empty,
NickName = randomIdentityEdit.Username ?? string.Empty,
FirstName = randomIdentityEdit.GetFieldValue(FieldKey.AliasFirstName),
LastName = randomIdentityEdit.GetFieldValue(FieldKey.AliasLastName),
BirthDate = randomIdentityEdit.GetFieldValue(FieldKey.AliasBirthdate),
Gender = randomIdentityEdit.GetFieldValue(FieldKey.AliasGender),
NickName = randomIdentityEdit.GetFieldValue(FieldKey.LoginUsername),
};
}
else
{
identity = new AliasVaultIdentity
{
FirstName = Obj.AliasFirstName ?? string.Empty,
LastName = Obj.AliasLastName ?? string.Empty,
BirthDate = Obj.AliasBirthDate ?? string.Empty,
Gender = Obj.AliasGender ?? string.Empty,
NickName = Obj.Username ?? string.Empty,
FirstName = aliasFirstName,
LastName = aliasLastName,
BirthDate = aliasBirthDate,
Gender = Obj.GetFieldValue(FieldKey.AliasGender),
NickName = Obj.GetFieldValue(FieldKey.LoginUsername),
};
}
Obj.Username = await JsInteropService.GenerateRandomUsernameAsync(identity);
Obj.SetFieldValue(FieldKey.LoginUsername, await JsInteropService.GenerateRandomUsernameAsync(identity));
}
/// <summary>
@@ -1127,4 +1113,5 @@ else
NavigationManager.NavigateTo("/items/" + Id);
}
}

View File

@@ -2164,6 +2164,10 @@ video {
padding-top: 2rem;
}
.pr-8 {
padding-right: 2rem;
}
.text-left {
text-align: left;
}