← Back to Component Index

ALPA Mobile — Backend API Mapping Report

App-wide component data requirements, API gaps, and model extension recommendations for the backend engineering team. Part 1 is the app-wide ask: the required API changes, the model rationale, and the rolled-up screen-set findings. Part 2 is the original Home deep dive, kept as an appendix.

Source: Figma file R6hPjBdjSIn6946qTJasUC — Home (node 4099:5230) + 9 screen-set analyses (Part 1 §3) Updated: 2026-07-10 16:30 ET Work Item: AB#1821 Status: Draft — pending rank API decision
10
Screen sets analyzed (Home + 9)
3
Active API changes (2 in progress · 1 pending client delivery)
1
Blocking gap (pilot rank — in progress with backend team)
1
Cross-control gap (Favorites)

Core Recommendation: Keep the Two Models Separate — Add Minimal Fields, Don't Merge

MenuItem and DocumentItem serve distinct, non-overlapping roles across the app. card-sm rows are MenuItem-backed navigation tiles. card-lg feed cards are DocumentItem-backed content cards. Keep them as separate models — neither should absorb the other's fields, and the two should not be merged. The changes below are small, additive model extensions (nullable, backward-compatible — not a merge): (1) add Rank (string, nullable) to UserInfo for the Welcome card greeting, (2) add Description (string, nullable) to MenuItem for the TextCard variant, and (3) a new shared, member-scoped Favorites capability keyed on (itemType, itemId) — the one cross-control gap. Separately, (4) a new DYK/FAQ content endpoint is required for the new "Did You Know?" feature (a new build, not a model change). See Part 1 §1.

Related work — data-source consolidation (AB#1709)

This report is supplemental to AB#1709 "Pilot Group Customizations — Consolidate data sources." Several findings here are alignment opportunities for that effort: pilot identity is split across UserInfo (auth: FirstName/LastName/MEC/LEC/BaseAirport) and Member (/api/member/getuser: memberNumber/preferences/mecLinks), and neither carries pilot Rank (Change 1). The per-pilot-group customizations map cleanly to the Mobile Menu API (MenuItem) — the same single-source direction AB#1709 is pursuing — rather than spreading across Member.MECLink / Preferences / hardcoded items. Use §1–§2 here as the alignment checklist.

Backend Handoff Tracker

This tracker is the canonical record of what has actually been communicated to the backend team — distinct from the technical analysis below, which captures what the mobile side has worked out regardless of whether it has been handed off yet.

ChangeAsk (one line)Handoff statusNext step / owner
Change 1UserInfo.Rank Add Rank (string, nullable) to the UserInfo auth/profile response for the Welcome card greeting. Handed off In progress with backend team.
Change 2MenuItem.Description Add description (string, nullable) to the MenuItem API response for the TextCard variant. Handed off In progress with backend team.
Change 3 — Favorites API New member-scoped favorites/bookmarks capability keyed on (itemType, itemId). Client delivered 2026-07-08 Gateway rollout pending Shipped: MobileMenu.ApiClient 1.0.7, mobile side wired (WI 2182) — Favorites screen, card-library heart-toggle, item-unavailable fallback. Live validation 2026-07-09: the endpoints 404 on the production gateway — service not reachable there yet; mobile side verified against mock pending backend rollout.
Change 4 — DYK/FAQ endpoint New endpoint returning the DYK/FAQ feed (net-new build). Abandoned — no handoff None — feature dropped 2026-06-24; kept here for the record only.
Change 5PageBanner Title/Subtitle/LogoImageLink Add three additive/nullable fields to the PageBanner response for the PromoBanner design (D43). ON HOLD — pending design-intent confirmation (DQ-26) Await the design answer to DQ-26 (is the structured banner a design requirement, or is a flat image per campaign adequate?). If a flat image is adequate, Change 5 is withdrawn; if structure is required, Change 5 proceeds as documented — backend team then pointed at the Change 5 handoff package section of this doc via the AB#1821 work item, no separate send.

Part 1 — App-Wide Findings & Asks

The cross-cutting backend work, ordered for action: the concrete API asks first, the model rationale behind them, then the screen-by-screen evidence and remaining questions. These apply across the whole app — the Home screen is detailed separately in Part 2.

1. Required API Changes (4 total)

Change 1 — Add Rank to UserInfo Blocking In progress — backend team

Why: Figma design shows "Welcome Captain Johnson". Rank is a first-class design element, not metadata.

Change: Add Rank (string, nullable) to the UserInfo auth/profile API response.

Possible values: "Captain", "First Officer", "Second Officer", null (omit greeting prefix if absent).

Impact: Auth service only. The client reads UserInfo.Rank when composing the welcome card (the Pilot Card domain control, which maps onto a Card). If null, the greeting falls back to "Welcome {FirstName} {LastName}".

Mobile model: UserInfo.cs — add public string? Rank { get; set; }

Change 2 — Add Description to MenuItem Minor In progress — backend team

Why: CardTextViewModel (backed by MenuItem) needs a description/subtitle field for the "Eyebrows" and "Simple" variants. card-sm itself doesn't show description, but TextCard does.

Change: Add description (string, nullable) to the MenuItem API response.

Impact: Menu API endpoint only. No schema-breaking change — nullable field, existing consumers unaffected.

Mobile model: MenuItem.cs — add public string Description { get; set; } = string.Empty;

Change 3 — Favorites / bookmarks capability Cross-control Client delivered 2026-07-08 Gateway rollout pending

Delivered 2026-07-08: MobileMenu.ApiClient 1.0.7 — GetItemTypesAsync/GetFavoritesAsync/AddFavoriteAsync/RemoveFavoriteAsync. Mobile side wired the same day (WI 2182): IFavoritesQueries port, the Favorites screen (heterogeneous card-lg/card-sm rendering + remove + empty state), heart-toggle across the card library (D31), and the Off the Radar item-unavailable fallback.

⚠ Document imagery gap (found in live validation 2026-07-09): every document in the live GET /v2/api/doc/getdocuments response carries image: null (42/42 sampled) — the card-lg document carousel has no real cover art to render and falls back to placeholder imagery. The Home feed design (card-lg) presumes per-document images; raise with backend alongside the AB#2133 dynamic-feed work so magazine covers/article art ship with the content.

⚠ Gateway rollout pending (found in live validation 2026-07-09): the delivered endpoints are not reachable on the production gatewayGET https://gateway.alpa.org/v2/api/mobilecontent/favorites, …/itemtypes, and POST all return 404 (verified with a valid member bearer token). This affects the whole MobileMenu.ApiClient surface: …/mobilecontent/menuitems/getallforuser 404s too (the app falls back to its offline menu list). The mobile slice is validated against the mock router; live end-to-end (including the dead-favorite → Off the Radar flow, which needs a server-seeded favorite) is blocked until the backend deploys the service behind the gateway. Follow up with the backend team on rollout timing.

Why (historical): The favorite (heart) affordance appears across many controls — MEC card-btn, card-md and media card-lg, FAQ / "Did You Know", and others. On Home it is hidden (decision D7); later screens show it. This was the single highest-leverage backend gap surfaced by the screen-set analysis — solved once, as a shared capability, not per control.

Delivered contract:

  • TogglePOST /api/mobilecontent/favorites { itemTypeId, itemId, sortOrder, title } and DELETE /api/mobilecontent/favorites/{itemTypeId}/{itemId}.
  • ReadGET /api/mobilecontent/favoritesid/userId/itemTypeId/itemTypeDescription/itemId/sortOrder/title + audit fields (ID-only-ish, not a hydrated payload — see below). GET /api/mobilecontent/favorites/itemtypes → the known item types.
  • Item identity — confirmed, exactly 3 types: 1=Menu Item, 2=Document Item, 3=Notification Center Message. No separate "button" type — card-btn vs card-sm is a presentation choice on Menu Item favorites, not data-driven.

Impact: New member-scoped endpoints, delivered. Each list reconciles its items against the member's favorites set client-side; the affected item view-models expose IsFavorite + a toggle command. The individual content models needed no change (they already carry stable IDs).

Mobile model: IFavoritesQueries (GetFavoritesAsync() / AddFavoriteAsync(Favorite) / RemoveFavoriteAsync(itemTypeId, itemId)) + IsFavorite/ShowFavorite/OnFavoriteToggle parameters on Card/CardSmall/HeroCard/ButtonCard (D31). IsFavorite now sources from the live API, not local storage.

Dedicated consumer: the Favorites screen (5287:15227, see favorites-screen-data-gap.html) renders the member's favorites by native control — card-lg / card-sm; Notification favorites render as a title-only row (no card component for that type). The condensed 5287:15454 list variant was not built this pass. Design point resolved: the read returns (itemType, itemId) + title only, not a hydrated payload — the client hydrates from IDocumentsQueries/IMenuQueries by itemId. sortOrder exists as the ordering field.

Change 4 — New DYK/FAQ content endpoint New feature · Blocking ABANDONED

Status (2026-06-24): This requirement has been abandoned. The "Did You Know?" feature is no longer planned. No endpoint will be built; remove from all planning.

Why (historical): The "Did You Know?" screen was a new feature — it does not exist in the app today (no UI, no endpoint, not hard-coded). Unlike Changes 1–3 (small additions to existing models/capabilities), this is a net-new backend build, so it is called out separately.

Change: Add a new endpoint returning the DYK/FAQ feed — a list of items carrying a date, headline, and body/answer (plus a nav target). The shape is already modeled by the unwired KCMFAQItem { Question, Answer } and KCMAlertItem { headline, story, publishDate } classes in KCM.cs — a reasonable contract starting point.

Impact: New endpoint + content store. Blocking — the "Did You Know?" feature cannot be built until this endpoint exists. The client builds the list (listy) against it. Favorites on these rows reuse the shared capability from Change 3.

Mobile model: a DYK/FAQ item model (Q/A + date/headline) and a service method (e.g. GetDidYouKnowAsync()); the KCMFAQItem/KCMAlertItem shapes can seed it.

Change 5 — Add Title / Subtitle / Logo to PageBanner Minor On hold — design intent (DQ-26, 2026-07-07)

Why: Figma's banner component (D43, PromoBanner) is a solid-navy-fill (#05273e) card with a title ("Survey Now Open"), a subtitle/expiry line ("Closes June 10 at 11:59PM PT"), a small logo image, and a dismiss (X) control — four distinct pieces of structured content, not one flat image. Today's implementation renders this as a single full-bleed image (FeaturedImageLink) with the title/subtitle text baked into the image pixels — confirmed 2026-07-06 against the live app: it's just an image plus a click-through target, no separate text fields.

Domain source identified: this is not a new Dynamic Feed ItemDto or a dedicated announcements service (the two candidates D43 originally floated) — it's the richer version of the existing, already-shipped PageBanner model (ALPAMobile.Domain/Data/Models/PageBanner.cs), fetched via GET /api/pagebanner/list (IRestService.GetPageBannersAsyncRestService.cs:1194) and resolved per page in BannerPageViewModel via PageBanner.GetBannerForPage(PageName, banners). This is a generic per-page banner slot already used by ~20+ pages (Home, Jumpseat, Known Crewmember, Member Resources, MEC/LEC, Flight Search, Settings, Notification Center, Advocacy — see the Key_*PageBanner constants in PageBanner.cs), not a Home-only or one-off feature. That also resolves D43's "is this even a library component" question per D40: it's a recurring, reusable pattern, not static one-off content — it correctly stays a catalogued domain control.

Change: Add to the PageBanner API response:

  • Title (string, nullable) — headline text, e.g. "Survey Now Open".
  • Subtitle (string, nullable) — secondary line, e.g. "Closes June 10 at 11:59PM PT". Open sub-question: a pre-formatted display string from the backend, or a structured ExpiresAt (DateTimeOffset) the client formats itself (better for timezone/localization consistency, matches the pattern already used elsewhere in FTDT)? Needs a decision before implementation.
  • LogoImageLink (string, nullable) — the small logo asset shown in the Figma design. Since the design's background is a solid fill (not a photo), this is likely a rename/repurpose of the existing FeaturedImageLink rather than an additional field — needs a decision on whether to reuse or add a second image field.
  • Dismiss capability — resolved (2026-07-06), client-only, no API change: default behavior is per-session — dismissal is held in-memory only, so if the app restarts and /api/pagebanner/list still returns the banner as active, it shows again. If a given banner ever needs a true "dismiss once, never show again" behavior, that's a per-device local-storage flag (the app's existing local Settings/Preferences mechanism), keyed by banner Id — not a member-scoped API call. Neither mode touches the backend; no favorites-style storage/endpoint is needed here.

Impact: Title/Subtitle/LogoImageLink are additive/nullable — non-breaking, PageBanner API only, benefits every page's banner slot (not just Home). Dismiss is entirely client-side (session in-memory, or per-device local storage for permanent dismiss) — no backend involvement.

Mobile model: PageBanner.cs — add Title, Subtitle (or ExpiresAt), LogoImageLink. Navigation (Path, DocType, DisplayMode, PageBannerNavigationExtensions.PageBannerClicked) is unaffected — click-through behavior stays as-is. Dismiss state is tracked client-side only: per-session in-memory by default, or a per-device local-storage flag keyed by Id if permanent dismiss is ever needed for a given banner — no new model field required on PageBanner itself.

Proposed diffALPAMobile.Domain/Data/Models/PageBanner.cs (mobile-side model; the backend response shape should mirror this 1:1):

 public class PageBanner
 {
     public const string Key_HomePageBanner = "Home Page";
     // ...existing Key_*PageBanner constants unchanged (~30 more)...
 
     public PageBanner() { }
 
     public int Id { get; set; }
     public string Page { get; set; } = string.Empty;
     public string ContentType { get; set; } = string.Empty;
     public string DocType { get; set; } = string.Empty;
     public string Source { get; set; } = string.Empty;
     public string FileId { get; set; } = string.Empty;
     public string Path { get; set; } = string.Empty;
     public string FeaturedImageLink { get; set; } = string.Empty;
     public string DisplayMode { get; set; } = string.Empty;
+
+    // -- New fields requested for the PromoBanner design (D43 / Change 5) --
+    public string? Title { get; set; }
+    public string? Subtitle { get; set; }
+    // Alternative to Subtitle -- pick ONE, do not add both:
+    // public DateTimeOffset? ExpiresAt { get; set; }
+    public string? LogoImageLink { get; set; }
 
     public static PageBanner? GetBannerForPage(string page, ObservableCollection<PageBanner> banners)
     { /* unchanged */ }
 }

Sample GET /api/pagebanner/list response — one entry, before/after (all three new fields nullable/omittable, so existing rows for the other ~20 pages need no changes):

// Before (today)
{
  "id": 42,
  "page": "Home Page",
  "contentType": "webpage",
  "docType": "weblink",
  "source": "",
  "fileId": "",
  "path": "https://ual.alpa.org/upa27-survey",
  "featuredImageLink": "https://cdn.alpa.org/banners/upa27-survey-full.png",
  "displayMode": "Normal"
}

// After (Change 5)
{
  "id": 42,
  "page": "Home Page",
  "contentType": "webpage",
  "docType": "weblink",
  "source": "",
  "fileId": "",
  "path": "https://ual.alpa.org/upa27-survey",
  "featuredImageLink": "https://cdn.alpa.org/banners/upa27-survey-full.png",
  "displayMode": "Normal",
  "title": "Survey Now Open",
  "subtitle": "Closes June 10 at 11:59PM PT",
  "logoImageLink": "https://cdn.alpa.org/logos/upa27-logo.png"
}

featuredImageLink is left in place deliberately — it's still used as-is by every other page's banner (a full-bleed image, no separate text fields needed there). logoImageLink is additive so those existing banners keep working unchanged; only rows that want the richer PromoBanner treatment populate the three new fields.

Handoff package (canonical — this doc is the handoff, 2026-07-07)

Status: ON HOLD — pending design-intent confirmation (DQ-26, 2026-07-07): if a supplied flat image per campaign is adequate, Change 5 is withdrawn; if structured content is required, Change 5 proceeds as documented. When it proceeds, this section is the handoff; no separate email/Teams send (decision 2026-07-07) — the backend team is pointed here via the AB#1821 work item (see the Backend Handoff Tracker). The cover text below is the handoff summary; the technical payload is the Proposed diff and Sample response blocks above — reference those verbatim rather than forking a copy.

Subject: PageBanner API — request for 3 additive fields (mobile UI Refresh, AB#1821)

Ask: add Title, Subtitle, LogoImageLink (all nullable) to the PageBanner response from GET /api/pagebanner/list. The UI Refresh banner design (Figma PromoBanner, decision D43) is structured content — a solid-navy card with a headline ("Survey Now Open"), a secondary/expiry line ("Closes June 10 at 11:59PM PT"), a small logo, and a dismiss control. Today the same content ships as one full-bleed image (FeaturedImageLink) with the text baked into the pixels, which blocks the new design, accessibility, and text scaling.

Non-breaking: all three fields are additive and nullable. Existing banner rows for the other ~20 pages need no changes and keep rendering via FeaturedImageLink exactly as today. Dismiss behavior is entirely client-side — no API change, no per-member storage.

Technical payload: the proposed C# model fields are the Proposed diff block above (the backend response shape should mirror ALPAMobile.Domain/Data/Models/PageBanner.cs 1:1), and the before/after JSON is the Sample GET /api/pagebanner/list response block above.

Two decisions we'd like backend input on before implementation:

  1. Subtitle (pre-formatted display string) vs ExpiresAt (DateTimeOffset the client formats)? We lean ExpiresAt for timezone/localization consistency — it matches the pattern already used elsewhere in the app — but a display string is fine if banner copy won't always be an expiry.
  2. LogoImageLink as a new field vs repurposing FeaturedImageLink? The new design's background is a solid fill, not a photo, so the row could reuse the existing field for the logo — but a separate field keeps existing full-bleed banners untouched. We lean separate/additive (as drafted).

The package closes by pointing the backend team back to this section for the full analysis and domain-source rationale (branch imp/blazor-hybrid).

2. MenuItem vs DocumentItem — Model Recommendation

ComponentBacked ByRationaleExtend?
card-sm (DART, Directories, etc.) MenuItem Navigation tiles. Title, Path, ImageSource, ShowOnHomeScreen, SortOrder all exist. Ordering and visibility controlled by backend. No — existing fields sufficient for card-sm
ButtonCard (Emergency Hotline) MenuItem Action button with Glyph + Title + Path. Identified by SpecialCode. No
TextCard (Eyebrows/Simple variants) MenuItem Navigation tile with richer display — needs Description. One nullable field addition. Yes — add Description
card-lg / HeroCard (feeds & standalone) DocumentItem One component (CardHeroViewModel) — the same card-lg whether shown standalone (e.g. Hotel Request) or inside a carousel/feed (CarouselViewModel.Items is a List<CardHeroViewModel>). Image, eyebrow, title; Scope + Category determine which documents appear. DocumentItem already has all fields. No
Welcome / Pilot Card (domain control → Card) IAuthentication + DocumentItem Greeting from UserInfo, contract link from DocumentItem. Hybrid — no single model extension needed. UserInfo: add Rank

Final Verdict: No Model Merging

Do not merge MenuItem and DocumentItem, or make one carry the other's fields. They model fundamentally different entities — navigation entries vs. content documents. Merging them would couple the navigation API to the content API and make both harder to evolve independently. Adding a small nullable field to a single model (Rank on UserInfo, Description on MenuItem) is a fine, backward-compatible extension — that is different from merging two models. Those two additions, the shared Favorites capability, and the new DYK/FAQ endpoint (all in §1) are the backend changes required to ship the app-wide design.

3. App-Wide Screen-Set Findings

This section rolls up the data-gap analysis of every non-Home screen set (Home itself is detailed in Part 2) — other screen sets — each has a full *-screen-data-gap.html report, and the at-a-glance rollup also lives in screen-mapping.htmlConsolidated Gaps. Bottom line: most sets are already data-ready; the cross-cutting backend ask is Favorites (Change 3), and the one net-new build is the DYK/FAQ endpoint (Change 4).

Data mapping by screen set

Screen setBacking endpoint(s) / model(s)Backend gap
MECGetDocuments (Scope/Category) · MenuItemFavorites card-md Description
KCMGetKCMAirlineItemsAsync → KCMAirlineItem · GetKCMAirportItemsAsync → KCMAirportItemNone
Jumpseat Flight FinderFlightSearchAsync → Flight/Leg · GetFlightSearchAirportsAsync · FindFlightInfoAsync · GetAirlineJumpseatPoliciesAsyncNone
FTDTDutyPeriod CRUD+sync · GetDutyPeriodsUpdatedAtAsync · client FAR-117 / Canadian calculatorsNone
Member ResourcesMenuItem nav · GetMemberAsync → Member (PDR prefill)PDR = web-scoped
NotificationsGetNotificationListAsync → NotificationCenterMessage · GetAllSubscriptionsForUserAsync · ToggleSubscriptionAsyncNone
Comms (Advocacy + Internal Comms)GetPACMemberInfoAsync → PACMember · GetCallToActionLinksAsync · GetDocumentListAsyncMeeting Report = web-scoped
Searchclient-side over loaded data (no search endpoint)None
FAQ / Did You KnowNEW feature — not in the existing app; needs a new API endpoint (no existing source). The Q/A + date/headline shape is already modeled by the unwired KCMFAQItem { Question, Answer } / KCMAlertItem { headline, story, publishDate } classes in KCM.cs — a contract starting point.New endpoint Favorites

Out of mobile scope — web application

Two forms in the mocks are web-app features accidentally surfaced in the mobile designs — no mobile endpoint needed; remove/hide on mobile: Submit a Pilot Data Request (Member Resources) and Submit Meeting Report (Advocacy — links to an online web form).

Open confirms (small)

4. Open Questions for Backend Team

  1. Pilot rank source: Where does rank (Captain / First Officer) come from in the ALPA data model? Auth service, member profile, or contract data?
  2. Feed section configuration: RESOLVED — same as #4: the feed sections (title, scope, category, sort) are driven by the GetDocuments API response (Scope / Category / Grouping), which the client maps from. No separate HomeFeedConfig endpoint needed.
  3. UAL MEC query: RESOLVED — no strict category mapping at this time; resolved like #2 / #4 via document Scope / Category / Grouping on the GetDocuments response. Content config determines which documents surface for the "UAL MEC" section.
  4. New & Notable query: RESOLVED — DocumentItem collection from the GetDocuments API, with Scope/Category/Grouping configured to match "New & Notable" so the proper items surface. Confirms content-driven configuration (no special DocumentItem flag needed).
  5. Emergency Hotline: LIKELY RESOLVED — driven by a mobile MenuItem; the client identifies it and renders the primary button. Confirm the identifying value (e.g. SpecialCode) on that MenuItem.
  6. Card-lg border accent color: DEFERRED — design team. The yellow variant (#f7bb0d) on some card-lg instances is a design question — waiting on Figma annotations before we can gauge whether it's data-driven (a DocumentItem field) or static styling. Tracked as DQ-2 in the Component Design Questions.
  7. Favorites / bookmarks: IN FLIGHT — pending API client delivery (2026-06-24). Backend team is building the capability. Mobile side will wire FavoritesService to the shape and calls in the delivered API client — do not pre-design the contract. FAQ / "Did You Know" is abandoned (Change 4 dropped), so the favorites dependency there is moot.
  8. PromoBanner dismiss persistence: RESOLVED (2026-07-06) — client-side only, no backend involvement. Default is per-session (in-memory; reappears on restart if the API still returns the banner active); permanent "never show again" is a per-device local-storage flag if a given banner ever needs it. See Change 5.

Part 2 — Home Screen (Appendix)

The original Home deep dive (Figma node 4099:5230), kept for reference. Every cross-cutting ask it surfaced is already rolled up into Part 1; this appendix shows the per-component derivation.

Home Screen Layout (Figma node 4099:5230)

The home screen feed is a vertical scroll of heterogeneous card types. The order and visibility of items is controlled by MenuItem.ShowOnHomeScreen and MenuItem.SortOrder.

#Figma ComponentMAUI ComponentData SourceStatus
1 welcome / Welcome thing Pilot Card (domain control) → CardViewModel IAuthentication + DocumentDatabase Gap: Rank
2 horizontal scrolling feed (UAL MEC) CarouselViewModel → card-lg (HeroCard items) DocumentItem (scope = pilot MEC) Mapped
3 Primary button (Emergency Hotline) ButtonViewModel MenuItem (SpecialCode = "emergency") Mapped
4 card-sm ×3–6 (DART, ALPA Directories, etc.) CardSmallViewModel : CardViewModel MenuItem (ShowOnHomeScreen = true) MenuItem-backed
5 horizontal scrolling feed (New & Notable) CarouselViewModel → card-lg (HeroCard items) DocumentItem (recent, sorted by PublishDate) Query TBD
6 card-lg / HeroCard (standalone on Home, e.g. Hotel Request) CardHeroViewModel DocumentItem Mapped
7 horizontal scrolling feed (Feed) CarouselViewModel → card-lg (HeroCard items) DocumentItem (general, scope-filtered) Query TBD

A1. Welcome Card

Welcome Captain Johnson View Contract

Background: #dfedf9 (--blue/100)
Figma node: I4099:5230;4038:893;5042:18877

PropertyFigma TextAPI SourceStatusNotes
Greeting line "Welcome Captain Johnson" IAuthentication.GetUserInfo() Partial FirstName + LastName available. Rank ("Captain") has no API source.
Pilot rank "Captain" None currently Blocked No rank field on the API. Member.classification carries a membership classification (e.g. "Active Member", "Retired Member", "Staff", "FOA") — not a pilot rank like Captain / First Officer.
Contract link text "View Contract" DocumentItem.Title via DataManager Mapped First contract doc for pilot MEC. Fallback: "View Contract".
Contract navigation Tap → document detail DocumentItem.FileID via SharedActionsService Mapped Resolved at presentation layer.
Background fill Design token --blue/100 Token #dfedf9. Note: our spec previously had #e8f4fd — corrected from Figma source.
Chevron (Group 568) Right arrow Static asset Static Visual affordance only — the chevron has no navigation of its own. Selecting the card navigates the same as View Contract (→ document detail, see Contract navigation above).

🚨 Blocking Gap — Pilot Rank

The Figma design shows "Welcome Captain Johnson". The current API has no field for pilot rank. Three options:

A2. card-sm (MenuItem Navigation Tile)

IMG
DART
IMG
ALPA Directories
Flight Pay Loss

Layout: 80px image left + flex title center
Image fallback bg: #dfedf9 (--blue/100) or #05273e (--blue/900) for dark tiles

card-sm directly represents a MenuItem row on the home screen. Instances seen in Figma: DART, ALPA Directories, Committee Calendar, Flight Pay Loss, My Payments, Infor Expense Management, Finance Reports, Meeting Request, MPS. All are standard app menu items with ShowOnHomeScreen = true.

PropertyFigma LayerAPI SourceModel FieldStatus
Title body / p — Lora SemiBold 16px MenuItem MenuItem.Title Mapped
Image / Icon img — 80×full-height MenuItem MenuItem.ImageSource Mapped
Image fallback bg Blue tint #dfedf9 or navy #05273e Design token Client-side fallback Client
Navigation target Card tap → page MenuItem MenuItem.Path Mapped
IsFavorite i-heart icon 18×18 Favorites API (Change 3) — Local Settings is the fallback Client-persisted state used only until the backend favorites API is delivered Client (fallback)
ShowOnHomeScreen Presence in feed MenuItem MenuItem.ShowOnHomeScreen Mapped
Sort order Position in list MenuItem MenuItem.SortOrder Mapped

Why no Description row: card-sm renders image + title only — it has no description. MenuItem.Description does not exist on the API today, but that missing field is a card-md / TextCard concern (see A5 below), not a card-sm gap.

ViewModel: CardSmallViewModel : CardViewModel

Add CardSmallViewModel backed entirely by MenuItem. No DocumentItem involvement. Properties: Title, ImageSource, Link (= Path), IsFavorite, ShowOnHomeScreen, SortOrder. This is the simplest component to implement — all properties exist on the current model except Description (needed only for the TextCard variant, not card-sm itself).

A3. card-lg in Horizontal Feeds (DocumentItem)

Three horizontal scroll feeds appear on the home screen: UAL MEC, New & Notable, and Feed. Each contains card-lg instances backed by DocumentItem. Card-lg structure confirmed from Figma: 200px image with 6px left border accent, eyebrow label, title.

IMG 200px
New & Notable 2026 Contract Highlights

Horizontal-feed card — 200px image, eyebrow + title, and a 6px left accent shown here in yellow (#f7bb0d) to flag the open gap: the accent can also be blue (#007bc2) and the variant logic that picks the colour is still TBD (see table below).
Backed by DocumentItem; appears in the UAL MEC / New & Notable / Feed scrollers.

Feed SectionSection TitleLink LabelQuery PatternStatus
UAL MEC "UAL MEC" (dynamic — pilot's MEC) "View My MEC" GetDocumentsForScopeCategoryAsync(userInfo.MEC, category) Category TBD
New & Notable "New & Notable" "View All" DocumentItem collection via GetDocuments — Scope/Category/Grouping configured to match "New & Notable" so the proper items surface Resolved
Feed "Feed" "View All" General content feed — scope and category to be defined with content team Content config TBD
card-lg PropertyFigma LayerModel FieldStatus
Image img 200px height, 6px left border DocumentItem.Image Mapped
Border accent color Blue #007bc2 or Yellow #f7bb0d Design token / category variant Variant logic TBD
Eyebrow Inter Bold 12px, uppercase, opacity 50% DocumentItem.Category or DocumentItem.Grouping Mapped
Title Lora SemiBold 20px, truncated with ellipsis DocumentItem.Title Mapped
IsFavorite i-heart 18×18 Favorites API (Change 3) — Client-persisted (Settings) is the fallback until the backend favorites API is delivered Client (fallback)
Navigation Card tap → document DocumentItem.FileID via SharedActionsService Mapped

⚠️ Feed Section Configuration — Backend Decision Needed

The three feed section titles ("UAL MEC", "New & Notable", "Feed") and their query filters are content configuration decisions. The mobile client should receive feed configuration (title, scope, category, sort).

A4. Primary Button (Emergency Hotline)

A full-width blue action button at the top of the feed. Figma shows "EMERGENCY HOTLINE" label with a phone icon. This maps to a MenuItem with a known SpecialCode.

Emergency Hotline

Full-width primary action — blue fill (#007bc2), white phone icon + label.
Identified by a known MenuItem.SpecialCode (legacy buffer, see below).

PropertySourceStatus
Label MenuItem.Title Mapped
Icon MenuItem.Glyph + MenuItem.GlyphFontFamily Mapped
Action MenuItem.Path (tel: URI or in-app screen) Mapped
Identification MenuItem.SpecialCode = "emergency" (or similar). Legacy — this SpecialCode-based identification is a buffer used only until dynamic (server-driven) UI is delivered, which will supersede it. SpecialCode value TBD (legacy buffer)

A5. card-md (CardText — Menu Text)

IMG 250×200
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor. View

Shown: the simplest Description variant — description + link only (no label or title set). The description is the card, so the MenuItem.Description gap is front and centre. The two richer variants add a title (Title) then an eyebrow label (Label); the full property set is listed below.

The CardText (card-md) composes a generic scaffold via a factory. Its Menu Text variant is backed by MenuItem and shows a description. The other text source, DocumentItem, already carries DocumentItem.Description — so only the MenuItem-backed variant has the gap.

PropertyFigma LayerAPI SourceModel FieldStatus
Label (optional) Eyebrow — Inter Bold 12px, uppercase Parent MenuItem MenuItem.Title of parent category Mapped
Title Lora SemiBold 18px MenuItem MenuItem.Title Mapped
Description Body text — rendered on card-md MenuItem MenuItem.Descriptionmissing (DocumentItem has one; MenuItem does not) API Change
Image img — 250×200 MenuItem MenuItem.ImageSource Mapped
Navigation Card tap → destination MenuItem MenuItem.Path Mapped

🚨 API Gap — MenuItem.Description

card-md renders a description line, but MenuItem has no Description field today (confirmed against the model: DocumentItem.Description exists, MenuItem has none). Add a nullable Description string to the MenuItem API response — a backward-compatible field addition. Null/empty collapses to the Title variant, so existing menu items render unchanged until content is supplied. This is the gap previously surfaced (misleadingly) under A2 card-sm — it belongs here, on the component that actually shows a description.

Part 3 — Data Sanitation: GetDocuments metadata alignment (backend ask)

Captured 2026-07-10 from the production /v2/api/doc/getdocuments response (20 scope nodes, 20 categories, 383 documents), while wiring the Blazor document surfaces. The client now works around each of these, but they belong to the payload — raising as API data-sanitation work so every consumer stops re-implementing the same workarounds.

#InconsistencyEvidence (production response)Suggested alignment
1Friendly-name field is inconsistent — no single field reliably carries the display name 15/20 categories: title == description == raw key (all three duplicated) · 2/20: friendly name in title (PRESIDENT → "From The President") · 1/20 (ALP MAG): title echoes the key and the friendly name appears only in description, as a sentence ("ALPA Magazine.") · 2/20: description empty One canonical displayName per category, always populated; description reserved for actual descriptions. (Client meanwhile resolves via CategoryItem.DisplayTitle: title-if-distinct → description → key.)
2Category keys mix naming styles ALLCAPS codes (KCM, FASTREAD, PRESIDENT) alongside display-style keys ("Canada Limits", "ALP MAG", "Press Release"); one key contains a slash (IRS/PBGC Limits) — a URL-encoding hazard since keys travel as query parameters Stable machine codes for keys (no spaces/slashes), display text in the name field
3Duplicate scope nodes — the same scope name appears as multiple sibling nodes ALPA ×6, MEC ×11, LEC ×2 (20 nodes for ~3 logical scopes); ALPA's categories are scattered across its 6 nodes. Root cause of the 5.0.13 documents-missing regression (BUG-1995 dedup dropped 41/42 docs) One node per scope with all categories inside, or an explicit disambiguating id if the duplication is intentional
4Document imagery absent image null/empty on 383/383 documents; category iconUrl present on only 14/20 Populate document imagery (the card-lg design renders a 200px photo; the app currently substitutes placeholder art). Already tracked as the imagery gap — folded here for the sanitation conversation
5path uses four different conventions (plus casing and encoding drift) 233 bare site-relative ("Documents/…" — incl. lowercase "documents/kcm/…" variants) · 115 absolute https · 32 DNN-relative ("~/media/…") · several paths with unencoded spaces ("documents/kcm/KCM FAQ.pdf") Canonical absolute URLs (or one documented relative convention + base), URL-encoded, consistent casing
6fileName never populated Empty on 383/383 documents — clients derive cache/display names from date + fileID Populate with the actual file name incl. extension
7docType vocabulary drift Both htm (10) and html (10) exist alongside pdf (248) and webpage (115) Collapse htm/html into one value; publish the closed vocabulary

Related: MobileMenu/mobilecontent gateway rollout (Part 1 probe matrix / Bug AB#2237) and MenuItem.Description population (backend Change 2) are the same class of alignment work on the menu side.