|
1 | 1 | @* Compact breadcrumbs shown in the header between the logo and switchers. |
2 | 2 | Computed from NavigationManager.Uri on every location change. *@ |
3 | 3 | @implements IDisposable |
| 4 | +@using Preflight.App.Content |
4 | 5 | @inject NavigationManager Nav |
5 | 6 | @inject IStringLocalizer<SharedResources> L |
6 | 7 |
|
|
62 | 63 | if (sub == "guided") |
63 | 64 | { |
64 | 65 | _crumbs.Add(new Crumb(L["Wizard.Guided.Title"], "wizard/guided/1")); |
65 | | - if (segments.Length >= 3 && int.TryParse(segments[2], out var step)) |
| 66 | + // Only emit a step crumb if the number is in range. The page |
| 67 | + // itself shows <NotFound /> for out-of-range steps; carrying |
| 68 | + // a "Step 999" crumb in the header on a 404 view is just noise. |
| 69 | + if (segments.Length >= 3 |
| 70 | + && int.TryParse(segments[2], out var step) |
| 71 | + && step >= 1 && step <= GuidedWizardTotalSteps) |
66 | 72 | { |
67 | 73 | _crumbs.Add(new Crumb($"{L["Common.Step"]} {step}", null)); |
68 | 74 | } |
69 | 75 | } |
70 | 76 | else if (sub == "presets") _crumbs.Add(new Crumb(L["Wizard.Presets.Title"], null)); |
71 | 77 | else if (sub == "review") _crumbs.Add(new Crumb(L["Wizard.Review.Title"], null)); |
| 78 | + // Any other /wizard/{x} is a 404 - no extra crumb. |
72 | 79 | } |
73 | 80 | break; |
74 | 81 |
|
|
77 | 84 | if (segments.Length >= 2) |
78 | 85 | { |
79 | 86 | var section = segments[1].ToLowerInvariant(); |
80 | | - _crumbs.Add(new Crumb(L[$"Advanced.Section.{section}"], null)); |
| 87 | + // Only render the section crumb when it's a known id; otherwise |
| 88 | + // IStringLocalizer falls back to echoing the raw key |
| 89 | + // ("Advanced.Section.test"), which reads as a leak in the header. |
| 90 | + if (SectionRegistry.AllSectionIds.Contains(section, StringComparer.Ordinal)) |
| 91 | + { |
| 92 | + _crumbs.Add(new Crumb(L[$"Advanced.Section.{section}"], null)); |
| 93 | + } |
81 | 94 | } |
82 | 95 | break; |
83 | 96 |
|
|
86 | 99 | if (segments.Length >= 2) |
87 | 100 | { |
88 | 101 | var topic = segments[1].ToLowerInvariant(); |
89 | | - _crumbs.Add(new Crumb(L[$"Advanced.Section.{topic}"], null)); |
| 102 | + // Same guard as /advanced/{x} - unknown slugs render <NotFound />, |
| 103 | + // breadcrumb stops at "Documentation" instead of leaking the key. |
| 104 | + if (SectionRegistry.AllSectionIds.Contains(topic, StringComparer.Ordinal)) |
| 105 | + { |
| 106 | + _crumbs.Add(new Crumb(L[$"Advanced.Section.{topic}"], null)); |
| 107 | + } |
90 | 108 | } |
91 | 109 | break; |
| 110 | + |
| 111 | + // Anything else under root (/foo, /xyz) is a 404 → render no breadcrumbs. |
| 112 | + // The Router's <NotFound> template handles the body; the header stays clean. |
92 | 113 | } |
93 | 114 | } |
94 | 115 |
|
| 116 | + // Keep in sync with GuidedWizard.TotalSteps. Lifted here so Breadcrumbs |
| 117 | + // doesn't need a reference to that page type just for one constant. |
| 118 | + private const int GuidedWizardTotalSteps = 5; |
| 119 | + |
95 | 120 | private sealed record Crumb(string Label, string? Href); |
96 | 121 | } |
0 commit comments