← Back to Index

Pilot Card - Data Source Analysis

Component: Card — Chevron variant (no title) · Pilot Card use case
Type: Domain control — composes the generic CardViewModel via PilotCardFactory (maps domain data → scaffold; it does not subclass the scaffold — see Decisions D11)
Last Updated: 2026-06-05

Renders the Card Chevron variant (heading + link + right chevron, no eyebrow/title/image, on a #dfedf9 surface). See the Chevron Variant section in card/card-component.html.

ASCII Diagram — Pilot Card Use Case

Shows how CardViewModel properties map to the rendered control using the pilot card as a concrete example.

                            HeaderText
                               │
                               ▼
                          MEC                          ◄── UserInfo.MEC ?? "ALPA"
                        ┌──────────────────────────────────────┐
                        │                                      │
   ContentText ───────► │  Welcome Johnson                     │ ◄── $"{FirstName} {LastName}" (no rank — see note)
                        │                                      │
   LinkText ──────────► │  View Contract ›                     │ ◄── contract?.Title ?? "View Contract"
   Link ──────────────► │  (tap navigates to destination)      │ ◄── contract?.FileID (DataManager)
                        │                                      │
                        └──────────────────────────────────────┘

   IsLinkVisible ──────►  Controls visibility of link row        ◄── contract is not null
   IsLoading ──────────►  Default strategy — no skeleton/overlay

Note: This document serves as a reference example for future presentation-layer phases.
When implementing specific use cases, follow this pattern to map domain data onto the generic CardViewModel via a factory — compose it, don't subclass it.


Summary

Property Status Source Notes
HeaderText ✅ Available IAuthentication.GetUserInfo().MEC Falls back to "ALPA"
ContentText ✅ Available UserInfo.FirstName + UserInfo.LastName Composed greeting — rank not available (see note below)
LinkText ✅ Resolved DocumentDatabase via DataManager First contract doc title for pilot's MEC scope
Link ✅ Resolved DocumentDatabase via DataManager First contract doc FileID/URL for pilot's MEC scope
IsLinkVisible ✅ Available ViewModel state Always visible for pilot card
IsLoading ✅ Available ViewModel state Default strategy (no skeleton)

⚠️ Pilot Rank — Confirmed Missing

UserInfo.UserType is populated by the auth API but contains membership tier only (STAFF or MEMBER). It does not contain pilot rank (Captain, First Officer, etc.).

No backend source for pilot rank currently exists. Options: 1. Omit rank from the greeting — display "Welcome {FirstName} {LastName}" 2. Request a new API field from the backend team to surface pilot rank 3. Derive from member data if rank is available elsewhere (e.g., contract or profile endpoint — needs investigation)

Current recommendation: Omit rank until a confirmed data source is identified.


Mapping — Factory + Domain Service

The domain control is split three ways: the scaffold CardViewModel stays domain-free; a pure factory maps domain data onto it (data in → VM out, no services, no async); a domain service owns the DI-injected async gathering, then hands the data to the factory. This keeps the library reusable and follows the RawRepresentationFactory<T> rule. (The factory must not hold an IServiceProvider — that would be a service-locator anti-pattern; see D11.)

// Scaffold — generic, domain-free (unchanged)
public class CardViewModel : SurfaceViewModel
{
    public string HeaderText { get; set; }
    public string ContentText { get; set; }
    public string LinkText { get; set; }
    public string Link { get; set; }
    public bool IsLinkVisible { get; set; }
}

// Domain control — PURE MAPPER: domain data in, scaffold VM out
public sealed class PilotCardFactory : RawRepresentationFactory<CardViewModel>
{
    public CardViewModel Create(UserInfo user, DocumentItem? contract) => new()
    {
        HeaderText    = user?.MEC ?? "ALPA",
        // rank omitted — UserInfo.UserType is STAFF/MEMBER, not pilot rank
        ContentText   = $"Welcome {user?.FirstName} {user?.LastName}",
        LinkText      = contract?.Title ?? "View Contract",
        Link          = contract?.FileID ?? string.Empty, // resolved by SharedActionsService
        IsLinkVisible = contract is not null,
    };
}

// Domain service — owns the async gathering (DI-injected), then maps
public sealed class PilotCardService(
    IAuthentication authentication,
    DataManager dataManager,
    PilotCardFactory factory)
{
    public async Task<CardViewModel> BuildAsync()
    {
        var user = authentication.GetUserInfo();

        // Resolve the contract via existing DocumentDatabase infrastructure:
        // categories for the pilot's MEC scope → "Contract" category → first doc
        var categories = await dataManager.GetDocumentCategoriesForScopeAsync(user.MEC);
        var contractCategory = categories?.FirstOrDefault(c =>
            c.Category.Contains("Contract", StringComparison.OrdinalIgnoreCase));

        DocumentItem? contract = null;
        if (contractCategory != null)
        {
            var docs = await dataManager.GetDocumentsForScopeCategoryAsync(
                user.MEC, contractCategory.Category);
            contract = docs?.FirstOrDefault();
        }

        return factory.Create(user, contract); // pure projection
    }
}

Properties

HeaderText

ContentText

LinkText

IsLinkVisible

IsLoading


Data Dependencies

Service Interface Notes
Authentication IAuthentication Provides UserInfo (MEC, name, UserType=STAFF/MEMBER)
DataManager DataManager GetDocumentCategoriesForScopeAsync + GetDocumentsForScopeCategoryAsync for contract lookup


Document Owner: ALPA Mobile Team
Purpose: Reference pattern for presentation-layer phase — do not use as active implementation spec