mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-10 00:06:08 -04:00
Refactor AddEdit flow to not use hardcoded definitions (#1404)
This commit is contained in:
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
87
apps/server/AliasVault.Client/Main/Models/SystemFieldEdit.cs
Normal file
87
apps/server/AliasVault.Client/Main/Models/SystemFieldEdit.cs
Normal 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; }
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2164,6 +2164,10 @@ video {
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
.pr-8 {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user