## Plan: Prospect list performance overhaul The current bottleneck is not just data volume; it is the combination of loading every prospect and every nested interaction at once, then rendering all cards in the browser. In the current flow, each page loads the full list from `ProspectService.GetProspectsAsync`, includes all `Images` and `Interactions` plus each `User`, and then `ProspectGrid` renders every card through `Repeater` without virtualization. With hundreds of records, the UI does repeated LINQ work per card and hits a lot of DOM work in one render. ### Recommended approach Use a two-part fix: reduce the server-side payload and virtualize the client-side rendering. The best gains will come from paginating or virtualizing the list, then trimming unnecessary nested load and repeated per-row calculations. ### Steps 1. Profile the bottleneck and cap the visible list size - Confirm the prospect pages are loading all records at once because `GetProspectsAsync` currently does `ToListAsync()` without paging or limit. - Add a page-size/virtualized flow so only the visible slice is fetched and rendered. - This change must happen in the page and service layer before any UI refactor. 2. Reduce EF payload in `ProspectService.GetProspectsAsync` - Update `FoodsharingSiegen.Server/Data/Service/ProspectService.cs` so it accepts pagination arguments (`skip`, `take`, optional filters) instead of returning the full unbounded list. - Keep the query as `AsNoTracking()` but avoid eager-loading all `Images` and every `User` for every prospect when the page only needs a summary view. - For the list pages, load only the subset needed for the grid and defer expensive image/user data until a detail action opens. - Filter and sort should happen server-side when possible to avoid moving hundreds of full entities into memory and then filtering again in the browser. 3. Replace the full list render with virtualization - Convert the repeated list in `FoodsharingSiegen.Server/Controls/ProspectGrid.razor` to a virtualized list or paged container, rather than rendering a full `` for all rows. - Keep the visible cards only; the hidden rows should not be in the DOM. - This removes the biggest DOM cost on pages with hundreds of entries. 4. Remove repeated per-row computation in the card components - In `FoodsharingSiegen.Server/Controls/InteractionRow.razor.cs`, the properties `Interactions`, `Done`, `Alert`, `NotNeeded` recalculate on every render from a list of nested items. With hundreds of cards this becomes expensive. - Replace repeated `Where()` chains with precomputed values from the loaded model or cached values at the prospect object level. - In `FoodsharingSiegen.Contracts/Entity/Prospect.cs`, avoid deriving `Complete` from `Interactions.Any()` on every render when a summary value is already known in the data model. - Keep the card logic free of repeated `ToList()` and `Any()` chains during render. 5. Tighten data shaping for each list page - The onboarding, verification, archive, and done pages all load the same full set of entities and then apply client-side filters with `ApplyFilter` and `ApplySort` in `FoodsharingSiegen.Shared/Helper/FilterHelper.cs`. - Move the expensive filtering/sorting to SQL or at least to the server-side query layer; reduce the list to a smaller result set before it reaches the UI. - Keep `ProspectList.ApplyFilter` only as a fallback for small local slices, not as the primary path for large lists. 6. Verify the improvement with focused checks - Measure page load time before and after with a realistic dataset of several hundred prospects. - Validate that the list stays responsive during scrolling and interaction changes. - Run the relevant .NET tests and render-check the prospect pages manually. ### Relevant files - `FoodsharingSiegen.Server/Data/Service/ProspectService.cs` — main query and database fetch point. - `FoodsharingSiegen.Server/Controls/ProspectGrid.razor` — full-list rendering path. - `FoodsharingSiegen.Server/Controls/ProspectContainer.razor` — per-card DOM and nested component rendering. - `FoodsharingSiegen.Server/Controls/InteractionRow.razor.cs` — repeated LINQ work in each row. - `FoodsharingSiegen.Contracts/Entity/Prospect.cs` — computed values that trigger repeated work. - `FoodsharingSiegen.Shared/Helper/FilterHelper.cs` — client-side list filtering/sorting on large sets. - `FoodsharingSiegen.Server/Pages/Prospects.razor`, `FoodsharingSiegen.Server/Pages/ProspectsAll.razor`, `FoodsharingSiegen.Server/Pages/ProspectsVerify.razor`, `FoodsharingSiegen.Server/Pages/ProspectsDone.razor` — list page orchestration and data flow. ### Key decision The highest-value fix is to stop returning and rendering all prospects at once. A virtualized/paged list with a trimmed query shape will provide the biggest speedup and should be the first implementation target. ### Optional follow-up improvements - Precompute a lightweight summary model for the grid instead of the full `Prospect` entity. - Add a server-side count query to support proper paging without loading every record. - Consider splitting the list into “summary” and “detail” data so the main dashboard never loads full nested relationship graphs by default.