diff --git a/.idea/.idea.blocktrust.CredentialWorkflow/.idea/inspectionProfiles/Project_Default.xml b/.idea/.idea.blocktrust.CredentialWorkflow/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..463ce50 --- /dev/null +++ b/.idea/.idea.blocktrust.CredentialWorkflow/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/config/applicationhost.config b/.idea/config/applicationhost.config new file mode 100644 index 0000000..4ef560c --- /dev/null +++ b/.idea/config/applicationhost.config @@ -0,0 +1,1019 @@ + + + + + + +
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Blocktrust.CredentialWorkflow.Web/Components/Features/Triggers/FormTrigger.razor b/Blocktrust.CredentialWorkflow.Web/Components/Features/Triggers/FormTrigger.razor index cf80aa1..34c76d8 100644 --- a/Blocktrust.CredentialWorkflow.Web/Components/Features/Triggers/FormTrigger.razor +++ b/Blocktrust.CredentialWorkflow.Web/Components/Features/Triggers/FormTrigger.razor @@ -1,15 +1,8 @@ @using Blocktrust.CredentialWorkflow.Core.Domain.ProcessFlow.Triggers -@using System.Text.RegularExpressions +@using Blocktrust.CredentialWorkflow.Web.Services @namespace Blocktrust.CredentialWorkflow.Web.Components.Features.Triggers
- @if (showToast) - { -
- @toastMessage -
- } -
@@ -25,44 +18,39 @@ @FormUrl
-

Form Fields

All fields are required by default
-
- @foreach (var param in Parameters) + @foreach (var param in fields) { -
+
@if (param.IsEditing) { + @bind="param.Name" + @bind:event="oninput" + @onchange="async () => await HandleFieldChange(param)"/> -
- - -
+ } else { @@ -76,35 +64,27 @@ edit
}
- @if (param.IsEditing) {
+ @bind="param.Description" + @bind:event="oninput" + @onchange="async () => await HandleFieldChange(param)"/> -
- - -
+ @bind="param.DefaultValue" + @bind:event="oninput" + @onchange="async () => await HandleFieldChange(param)"/>
} else @@ -113,12 +93,6 @@ {
@param.Description
} - @if (param.AllowedValues?.Any() == true) - { -
- Allowed values: @string.Join(", ", param.AllowedValues) -
- } @if (!string.IsNullOrEmpty(param.DefaultValue)) {
@@ -126,34 +100,16 @@
} } - - @if (!string.IsNullOrEmpty(param.ValidationError)) - { -
@param.ValidationError
- }
}
-
- - - @if (Parameters.Any(p => !p.IsEditing && !string.IsNullOrEmpty(p.Name))) - { - - }
@@ -161,39 +117,43 @@ [Parameter] public TriggerInputForm TriggerInput { get; set; } = null!; [Parameter] public EventCallback OnChange { get; set; } [Parameter] public Guid WorkflowId { get; set; } - [Inject] private NavigationManager NavigationManager { get; set; } = default!; - [Inject] private IJSRuntime JSRuntime { get; set; } = default!; - - private string? toastMessage; - private bool showToast; - private List Parameters { get; set; } = new(); + [Inject] private ClipboardService ClipboardService { get; set; } = default!; + private List fields = new(); private string FormUrl => $"{NavigationManager.BaseUri.TrimEnd('/')}/form/{WorkflowId}"; + private bool isInitialized = false; protected override void OnInitialized() { - InitializeParameters(); + if (!isInitialized) + { + LoadFields(); + isInitialized = true; + } } protected override void OnParametersSet() { - InitializeParameters(); + if (!isInitialized) + { + LoadFields(); + isInitialized = true; + } } - private void InitializeParameters() + private void LoadFields() { - Parameters.Clear(); - foreach (var param in TriggerInput.Parameters) + if (TriggerInput?.Parameters == null) return; + + fields = TriggerInput.Parameters.Select(p => new FormField { - Parameters.Add(new FieldData - { - Name = param.Key, - Type = param.Value.Type.ToString().ToLower(), - Description = param.Value.Description, - AllowedValues = param.Value.AllowedValues, - DefaultValue = param.Value.DefaultValue - }); - } + Name = p.Key, + Type = p.Value.Type.ToString().ToLower(), + Description = p.Value.Description ?? "", + DefaultValue = p.Value.DefaultValue, + IsEditing = false, + OriginalName = p.Key // Store original name for tracking + }).ToList(); } private string GetDisplayType(string type) => type switch @@ -205,153 +165,83 @@ _ => type }; - private void AddParameter() + private void AddField() { - var newParam = new FieldData + var newField = new FormField { IsEditing = true, - TempName = "", - TempType = "string", - TempDescription = "" + Type = "string", + Name = "", + OriginalName = null }; - Parameters.Add(newParam); - } - - private void StartEdit(FieldData param) - { - param.IsEditing = true; - param.TempName = param.Name; - param.TempType = param.Type; - param.TempDescription = param.Description; - param.TempDefaultValue = param.DefaultValue; - param.TempAllowedValuesText = param.AllowedValues != null ? string.Join("\n", param.AllowedValues) : null; - param.ValidationError = null; + fields.Add(newField); + UpdateTriggerInput(); } - private void CancelEdit(FieldData param) + private void StartEdit(FormField field) { - if (string.IsNullOrEmpty(param.Name)) - { - Parameters.Remove(param); - } - else - { - param.IsEditing = false; - param.ValidationError = null; - } + field.IsEditing = true; + field.OriginalName = field.Name; + StateHasChanged(); } - private async Task SaveParameter(FieldData param) + private async Task HandleFieldChange(FormField field) { - if (ValidateParameter(param)) + if (!string.IsNullOrWhiteSpace(field.Name)) { - param.Name = param.TempName!; - param.Type = param.TempType!; - param.Description = param.TempDescription ?? ""; - param.DefaultValue = param.TempDefaultValue; - param.AllowedValues = param.TempAllowedValues; - param.IsEditing = false; - param.ValidationError = null; - await UpdateTriggerParameters(); + UpdateTriggerInput(); + await OnChange.InvokeAsync(); } } - private bool ValidateParameter(FieldData param) + private void CancelEdit(FormField field) { - var nameValidationRegex = new Regex("^[a-zA-Z][a-zA-Z0-9]*$"); - - if (string.IsNullOrWhiteSpace(param.TempName)) - { - param.ValidationError = "Field name cannot be empty"; - return false; - } - - if (!nameValidationRegex.IsMatch(param.TempName)) + if (string.IsNullOrEmpty(field.OriginalName)) { - param.ValidationError = "Field name must start with a letter and contain only letters and numbers"; - return false; + fields.Remove(field); } - - if (Parameters.Any(p => p != param && p.Name == param.TempName)) - { - param.ValidationError = "Field name must be unique"; - return false; - } - - if (string.IsNullOrWhiteSpace(param.TempDescription)) + else { - param.ValidationError = "Description cannot be empty"; - return false; + field.Name = field.OriginalName; + field.IsEditing = false; } - - return true; + UpdateTriggerInput(); } - private async Task RemoveParameter(FieldData param) + private async Task RemoveField(FormField field) { - Parameters.Remove(param); - await UpdateTriggerParameters(); + fields.Remove(field); + UpdateTriggerInput(); + await OnChange.InvokeAsync(); } - private async Task UpdateTriggerParameters() + private void UpdateTriggerInput() { - // Clear existing parameters - var existingKeys = TriggerInput.Parameters.Keys.ToList(); - foreach (var key in existingKeys) - { - TriggerInput.Parameters.Remove(key); - } - - // Update with current parameters - foreach (var param in Parameters.Where(p => !p.IsEditing && !string.IsNullOrEmpty(p.Name))) + TriggerInput.Parameters.Clear(); + foreach (var field in fields.Where(f => !string.IsNullOrEmpty(f.Name))) { - TriggerInput.Parameters[param.Name] = new ParameterDefinition + TriggerInput.Parameters[field.Name] = new ParameterDefinition { - Type = Enum.Parse(param.Type, true), - Description = param.Description, - AllowedValues = param.AllowedValues, - DefaultValue = param.DefaultValue, - Required = true // All form fields are required by default + Type = Enum.Parse(field.Type, true), + Description = field.Description, + DefaultValue = field.DefaultValue, + Required = true }; } - - await OnChange.InvokeAsync(); } private async Task CopyToClipboard(string text) { - await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", text); - toastMessage = "Copied to clipboard!"; - showToast = true; - StateHasChanged(); - await Task.Delay(2000); - showToast = false; - StateHasChanged(); + await ClipboardService.CopyTextToClipboard(text); } - private class FieldData + private class FormField { public string Name { get; set; } = ""; public string Type { get; set; } = "string"; public string Description { get; set; } = ""; - public string[]? AllowedValues { get; set; } public string? DefaultValue { get; set; } - public string? ValidationError { get; set; } - - // Editing state public bool IsEditing { get; set; } - public string? TempName { get; set; } - public string? TempType { get; set; } - public string? TempDescription { get; set; } - public string? TempDefaultValue { get; set; } - private string? _tempAllowedValuesText; - public string? TempAllowedValuesText - { - get => _tempAllowedValuesText ?? (AllowedValues != null ? string.Join("\n", AllowedValues) : null); - set => _tempAllowedValuesText = value; - } - public string[]? TempAllowedValues => !string.IsNullOrWhiteSpace(TempAllowedValuesText) - ? TempAllowedValuesText.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) - : null; + public string? OriginalName { get; set; } // Track original name for edit operations } } \ No newline at end of file diff --git a/Blocktrust.CredentialWorkflow.Web/Components/Features/Triggers/HttpRequestTrigger.razor b/Blocktrust.CredentialWorkflow.Web/Components/Features/Triggers/HttpRequestTrigger.razor index f2211d2..1671156 100644 --- a/Blocktrust.CredentialWorkflow.Web/Components/Features/Triggers/HttpRequestTrigger.razor +++ b/Blocktrust.CredentialWorkflow.Web/Components/Features/Triggers/HttpRequestTrigger.razor @@ -36,7 +36,7 @@ + @bind="param.Name" + @bind:event="oninput" + @onchange="async () => await HandleParameterChange(param)" /> -
- - -
+ } else { @@ -96,13 +89,10 @@ @onclick="() => StartEdit(param)"> edit - @if (!param.IsRequired) - { - - } + } @@ -112,17 +102,18 @@ + @bind="param.Description" + @bind:event="oninput" + @onchange="async () => await HandleParameterChange(param)" /> } else if (!string.IsNullOrEmpty(param.Description)) {
@param.Description
} - @if (!string.IsNullOrEmpty(param.ValidationError)) + @if (!string.IsNullOrEmpty(param.ValidationMessage)) { -
@param.ValidationError
+
@param.ValidationMessage
} } @@ -158,140 +149,135 @@ private string? toastMessage; private bool showToast; - private List Parameters { get; set; } = new(); + private List parameters = new(); private string FullUrl => $"{NavigationManager.BaseUri.TrimEnd('/')}/api/workflow/{WorkflowId}"; + private bool isInitialized = false; protected override void OnInitialized() { - InitializeParameters(); + if (!isInitialized) + { + LoadParameters(); + isInitialized = true; + } } protected override void OnParametersSet() { - InitializeParameters(); + if (!isInitialized) + { + LoadParameters(); + isInitialized = true; + } } - private void InitializeParameters() + private void LoadParameters() { - Parameters.Clear(); - foreach (var param in TriggerInput.Parameters) + if (TriggerInput?.Parameters == null) return; + + parameters = TriggerInput.Parameters.Select(p => new Parameter { - Parameters.Add(new ParameterData - { - Name = param.Key, - Type = param.Value.Type.ToString().ToLower(), - Description = param.Value.Description, - IsRequired = param.Value.Required - }); - } + Name = p.Key, + Type = p.Value.Type.ToString().ToLower(), + Description = p.Value.Description ?? "", + IsEditing = false, + OriginalName = p.Key + }).ToList(); } - private async Task OnInputChanged() + private async Task OnMethodChanged() { await UpdateTriggerParameters(); } - private void AddParameter() + private async Task AddParameter() { - var newParam = new ParameterData + var newParam = new Parameter { IsEditing = true, - TempName = "", - TempType = "string", - TempDescription = "" + Type = "string", + Name = "" }; - Parameters.Add(newParam); + parameters.Add(newParam); + await UpdateTriggerParameters(); } - private void StartEdit(ParameterData param) + private void StartEdit(Parameter param) { param.IsEditing = true; - param.TempName = param.Name; - param.TempType = param.Type; - param.TempDescription = param.Description; - param.ValidationError = null; + param.OriginalName = param.Name; + StateHasChanged(); } - private void CancelEdit(ParameterData param) + private async Task HandleParameterChange(Parameter param) { - if (string.IsNullOrEmpty(param.Name)) - { - Parameters.Remove(param); - } - else + if (ValidateParameter(param)) { - param.IsEditing = false; - param.ValidationError = null; + param.IsEditing = true; + param.ValidationMessage = null; + UpdateTriggerParameters(); + await OnChange.InvokeAsync(); } } - private async Task SaveParameter(ParameterData param) + private void CancelEdit(Parameter param) { - if (ValidateParameter(param)) + if (string.IsNullOrEmpty(param.OriginalName)) + { + parameters.Remove(param); + } + else { - param.Name = param.TempName!; - param.Type = param.TempType!; - param.Description = param.TempDescription; + param.Name = param.OriginalName; param.IsEditing = false; - param.ValidationError = null; - await UpdateTriggerParameters(); } + UpdateTriggerParameters(); } - private bool ValidateParameter(ParameterData param) + private bool ValidateParameter(Parameter param) { var nameValidationRegex = new Regex("^[a-zA-Z][a-zA-Z0-9]*$"); - if (string.IsNullOrWhiteSpace(param.TempName)) + if (string.IsNullOrWhiteSpace(param.Name)) { - param.ValidationError = "Parameter name cannot be empty"; + param.ValidationMessage = "Parameter name cannot be empty"; return false; } - if (!nameValidationRegex.IsMatch(param.TempName)) + if (!nameValidationRegex.IsMatch(param.Name)) { - param.ValidationError = "Parameter name must start with a letter and contain only letters and numbers"; + param.ValidationMessage = "Parameter name must start with a letter and contain only letters and numbers"; return false; } - if (Parameters.Any(p => p != param && p.Name == param.TempName)) + if (parameters.Any(p => p != param && p.Name == param.Name)) { - param.ValidationError = "Parameter name must be unique"; + param.ValidationMessage = "Parameter name must be unique"; return false; } return true; } - private async Task RemoveParameter(ParameterData param) + private async Task RemoveParameter(Parameter param) { - if (!param.IsRequired) - { - Parameters.Remove(param); - await UpdateTriggerParameters(); - } + parameters.Remove(param); + UpdateTriggerParameters(); + await OnChange.InvokeAsync(); } private async Task UpdateTriggerParameters() { - // Clear existing parameters - var existingKeys = TriggerInput.Parameters.Keys.ToList(); - foreach (var key in existingKeys) - { - TriggerInput.Parameters.Remove(key); - } - - // Update with current parameters - foreach (var param in Parameters.Where(p => !p.IsEditing && !string.IsNullOrEmpty(p.Name))) + TriggerInput.Parameters.Clear(); + foreach (var param in parameters.Where(p => !string.IsNullOrEmpty(p.Name))) { TriggerInput.Parameters[param.Name] = new ParameterDefinition { Type = Enum.Parse(param.Type, true), - Description = param.Description ?? $"Parameter: {param.Name}", - Required = param.IsRequired + Description = param.Description, + Required = true }; } - await OnChange.InvokeAsync(); } @@ -301,18 +287,18 @@ { var command = $"curl -X {TriggerInput.Method} \"{FullUrl}\""; - var parameters = Parameters.Where(p => !p.IsEditing && !string.IsNullOrEmpty(p.Name)); - if (!parameters.Any()) return command; + var validParams = parameters.Where(p => !string.IsNullOrEmpty(p.Name)); + if (!validParams.Any()) return command; if (TriggerInput.Method == "GET") { - var queryParams = parameters.Select(p => $"{p.Name}={HttpUtility.UrlEncode($"value_{p.Name}")}"); + var queryParams = validParams.Select(p => $"{p.Name}={HttpUtility.UrlEncode($"value_{p.Name}")}"); command += $"?{string.Join("&", queryParams)}"; } else { command += " \\\n -H \"Content-Type: application/json\""; - var body = parameters.ToDictionary(p => p.Name, p => $"value_{p.Name}"); + var body = validParams.ToDictionary(p => p.Name, p => $"value_{p.Name}"); var jsonBody = System.Text.Json.JsonSerializer.Serialize(body, new System.Text.Json.JsonSerializerOptions { WriteIndented = true @@ -331,9 +317,9 @@ var schema = new { type = "object", - required = Parameters.Where(p => p.IsRequired && !p.IsEditing).Select(p => p.Name).ToList(), - properties = Parameters - .Where(p => !p.IsEditing && !string.IsNullOrEmpty(p.Name)) + required = parameters.Where(p => !string.IsNullOrEmpty(p.Name)).Select(p => p.Name).ToList(), + properties = parameters + .Where(p => !string.IsNullOrEmpty(p.Name)) .ToDictionary( p => p.Name, p => new @@ -362,18 +348,13 @@ StateHasChanged(); } - private class ParameterData + private class Parameter { public string Name { get; set; } = ""; public string Type { get; set; } = "string"; - public string? Description { get; set; } - public bool IsRequired { get; set; } - public string? ValidationError { get; set; } - - // Editing state + public string Description { get; set; } = ""; public bool IsEditing { get; set; } - public string? TempName { get; set; } - public string? TempType { get; set; } - public string? TempDescription { get; set; } + public string? ValidationMessage { get; set; } + public string? OriginalName { get; set; } } } \ No newline at end of file diff --git a/Blocktrust.CredentialWorkflow.Web/wwwroot/app.css b/Blocktrust.CredentialWorkflow.Web/wwwroot/app.css index 9d5f6a7..8d40d1e 100644 --- a/Blocktrust.CredentialWorkflow.Web/wwwroot/app.css +++ b/Blocktrust.CredentialWorkflow.Web/wwwroot/app.css @@ -588,10 +588,6 @@ video { } } -.pointer-events-none { - pointer-events: none; -} - .invisible { visibility: hidden; } @@ -620,11 +616,6 @@ video { inset: 0px; } -.inset-y-0 { - top: 0px; - bottom: 0px; -} - .bottom-0 { bottom: 0px; } @@ -802,10 +793,6 @@ video { display: inline-block; } -.inline { - display: inline; -} - .flex { display: flex; } @@ -1046,6 +1033,10 @@ video { align-items: flex-start; } +.items-end { + align-items: flex-end; +} + .items-center { align-items: center; } @@ -1142,6 +1133,10 @@ video { border-color: rgb(226 232 240 / var(--tw-divide-opacity)); } +.self-end { + align-self: flex-end; +} + .overflow-hidden { overflow: hidden; } @@ -1326,6 +1321,11 @@ video { background-color: rgb(239 246 255 / var(--tw-bg-opacity)); } +.bg-blue-500 { + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); +} + .bg-gray-100 { --tw-bg-opacity: 1; background-color: rgb(243 244 246 / var(--tw-bg-opacity)); @@ -1564,10 +1564,6 @@ video { padding-right: 2.5rem; } -.pr-3 { - padding-right: 0.75rem; -} - .pr-4 { padding-right: 1rem; } @@ -2070,11 +2066,6 @@ a { color: rgb(20 83 45 / var(--tw-text-opacity)); } -.hover\:text-red-500:hover { - --tw-text-opacity: 1; - color: rgb(239 68 68 / var(--tw-text-opacity)); -} - .hover\:text-red-700:hover { --tw-text-opacity: 1; color: rgb(185 28 28 / var(--tw-text-opacity));