Enhance CareerMapping component to reflect career fields instead of careers

Updated the CareerMapping component to improve clarity by changing terminology from "careers" to "career fields" throughout the UI. Enhanced the diagram generation logic to filter events based on related career fields, ensuring accurate representation of relationships. Added styling for event and field nodes in the Mermaid diagram for better visual distinction.
This commit is contained in:
2025-12-29 10:54:30 -05:00
parent 2c9aa1c223
commit 7266ab609b
3 changed files with 383 additions and 26 deletions
@@ -2,6 +2,8 @@
@attribute [Authorize]
@using Microsoft.EntityFrameworkCore
@using WebApp.Components.Shared.Components
@using Core.Utility
@using Core.Entities
@inject AppDbContext Context
<PageHeader
@@ -20,23 +22,24 @@
else if (string.IsNullOrWhiteSpace(_mermaidDefinition))
{
<MudPaper Elevation="2" Class="pa-6">
<MudText Typo="Typo.h6" Class="mb-4">No Career Mappings Found</MudText>
<MudText Typo="Typo.h6" Class="mb-4">No Career Field Mappings Found</MudText>
<MudText Typo="Typo.body1" Class="mud-text-secondary">
No events have related careers assigned. Edit events to add related careers.
No events have related careers assigned that match any career fields. Edit events to add related careers.
</MudText>
</MudPaper>
}
else
{
<MudPaper Elevation="2" Class="pa-6">
<MudText Typo="Typo.h6" Class="mb-4">Event-Career Relationships</MudText>
<MudText Typo="Typo.h6" Class="mb-4">Event-Career Field Relationships</MudText>
<MudText Typo="Typo.body2" Class="mb-4 mud-text-secondary">
This diagram shows the connections between events and their related careers.
Events are shown on the left, careers on the right.
This diagram shows the connections between events and their related career fields.
Events are shown on the left, career fields on the right. Career fields are clusters of related careers.
</MudText>
<div class="mermaid-diagram-container" style="overflow-x: auto; min-height: 400px;">
<MermaidDiagram Definition="@_mermaidDefinition" />
</div>
<MudText Style="white-space:pre-wrap;">@_mermaidDefinition</MudText>
</MudPaper>
}
@@ -61,9 +64,14 @@ else
.OrderBy(e => e.Name)
.ToListAsync();
if (events.Any())
// Filter to only events that have career fields (after matching)
var eventsWithFields = events
.Where(e => CareerFieldDefinitions.GetRelatedCareerFields(e.RelatedCareers).Any())
.ToList();
if (eventsWithFields.Any())
{
_mermaidDefinition = GenerateMermaidDiagram(events);
_mermaidDefinition = GenerateMermaidDiagram(eventsWithFields);
}
}
finally
@@ -79,12 +87,15 @@ else
// Dictionary to track node IDs and labels (to avoid duplicates)
var eventNodeIds = new Dictionary<int, (string Id, string Label)>();
var careerNodeIds = new Dictionary<int, (string Id, string Label)>();
var careerCounter = 1;
var fieldNodeIds = new Dictionary<int, (string Id, string Label)>();
var fieldCounter = 1;
var eventCounter = 1;
// First pass: collect all unique nodes
foreach (var evt in events)
// Dictionary to track which events connect to which career fields
var eventToFields = new Dictionary<int, HashSet<CareerField>>();
// First pass: collect all unique nodes and determine relationships
foreach (var evt in events.OrderBy(e => e.Name))
{
if (!eventNodeIds.ContainsKey(evt.Id))
{
@@ -93,40 +104,57 @@ else
eventNodeIds[evt.Id] = (eventNodeId, eventLabel);
}
foreach (var career in evt.RelatedCareers)
// Get related career fields for this event's careers
var relatedFields = CareerFieldDefinitions.GetRelatedCareerFields(evt.RelatedCareers);
if (relatedFields.Any())
{
if (!careerNodeIds.ContainsKey(career.Id))
eventToFields[evt.Id] = new HashSet<CareerField>(relatedFields);
// Track career field nodes
foreach (var field in relatedFields)
{
var careerNodeId = $"C{careerCounter++}";
var careerLabel = EscapeMermaidLabel(career.Name);
careerNodeIds[career.Id] = (careerNodeId, careerLabel);
if (!fieldNodeIds.ContainsKey(field.Id))
{
var fieldNodeId = $"F{fieldCounter++}";
var fieldLabel = EscapeMermaidLabel(field.Name);
fieldNodeIds[field.Id] = (fieldNodeId, fieldLabel);
}
}
}
}
// Second pass: define all nodes
// Second pass: define all event nodes (blue)
foreach (var (id, (nodeId, label)) in eventNodeIds.OrderBy(e => e.Value.Label))
{
builder.AppendLine($" {nodeId}[\"{label}\"]");
builder.AppendLine($" {nodeId}[\"{label}\"]:::eventNode");
}
foreach (var (id, (nodeId, label)) in careerNodeIds.OrderBy(c => c.Value.Label))
// Third pass: define all career field nodes (green)
foreach (var (id, (nodeId, label)) in fieldNodeIds.OrderBy(f => f.Value.Label))
{
builder.AppendLine($" {nodeId}[\"{label}\"]");
builder.AppendLine($" {nodeId}[\"{label}\"]:::fieldNode");
}
// Third pass: define all edges
// Fourth pass: define all edges (events on left, fields on right)
foreach (var evt in events.OrderBy(e => e.Name))
{
var currentEventNodeId = eventNodeIds[evt.Id].Id;
foreach (var career in evt.RelatedCareers.OrderBy(c => c.Name))
if (eventToFields.TryGetValue(evt.Id, out var fields))
{
var currentCareerNodeId = careerNodeIds[career.Id].Id;
builder.AppendLine($" {currentEventNodeId} --> {currentCareerNodeId}");
var currentEventNodeId = eventNodeIds[evt.Id].Id;
foreach (var field in fields.OrderBy(f => f.Name))
{
var currentFieldNodeId = fieldNodeIds[field.Id].Id;
builder.AppendLine($" {currentEventNodeId} --> {currentFieldNodeId}");
}
}
}
// Add styling for different colors
builder.AppendLine("");
builder.AppendLine("classDef eventNode fill:#4A90E2,stroke:#2E5C8A,stroke-width:2px,color:#fff");
builder.AppendLine("classDef fieldNode fill:#50C878,stroke:#2D8659,stroke-width:2px,color:#fff");
return builder.ToString();
}