Skip to content

Coffee Bean Review Dashboard

Goal

Build a custom dashboard page (not a CRUD controller) that displays cards with pre-filtered links to the existing CoffeeBeanCrudController, organized by status and other useful filters.

Problem

The team needs an easy way to access different coffee bean reviews filtered by status without having to manually set filters each time. They want "prefilled filters" - quick access links that take them directly to filtered views.

Solution

Create a custom dashboard route with a dedicated template that shows: - Status-based filter cards (OK, Partial Data, Invalid Data, Extraction Failed, Discarded) - Each card displays the count and a "View All" link with pre-filled status filter - Additional filter sections for Availability and Origin Type

Implementation Steps

1. Clean Up Incorrect Implementation

  • Delete src/Controller/Admin/CoffeeBeanReviewCrudController.php
  • This CRUD controller approach was wrong - we don't need another CRUD interface
  • Delete or keep templates/admin/review/crud/index.html.twig
  • The availability toggle template - might be useful later, or can be removed

2. Add Review Dashboard Method to DashboardController

File: src/Controller/Admin/DashboardController.php

Add a new route method:

#[Route('/admin/review-dashboard', name: 'admin_review_dashboard')]
public function reviewDashboard(): Response
{
    // Query database for status counts
    $statusCounts = $this->entityManager
        ->getRepository(CoffeeBean::class)
        ->createQueryBuilder('cb')
        ->select('cb.status, COUNT(cb.id) as count')
        ->groupBy('cb.status')
        ->getQuery()
        ->getResult();

    // Convert to associative array for template
    $counts = [];
    foreach ($statusCounts as $row) {
        $counts[$row['status']->value] = $row['count'];
    }

    // Ensure AdminContext exists (required for FrankenPHP)
    $request = $this->container->get('request_stack')->getCurrentRequest();
    if ($this->adminContextProvider->getContext() === null && $request) {
        $adminContext = $this->adminContextFactory->create($request, $this, null);
        $request->attributes->set(EA::CONTEXT_REQUEST_ATTRIBUTE, $adminContext);
    }

    return $this->render('admin/review_dashboard.html.twig', [
        'status_counts' => $counts,
    ]);
}

3. Create Review Dashboard Template

File: templates/admin/review_dashboard.html.twig

Structure: - Extends @EasyAdmin/layout.html.twig - Status cards section (4 columns, each card shows count + link) - Availability filter section (2 buttons: Available/Unavailable) - Origin Type filter section (2 buttons: Single Origin/Blend)

Filter URL Pattern:

{{ ea_url()
    .setController('App\\Controller\\Admin\\CoffeeBeanCrudController')
    .setAction('index')
    .set('filters[status][comparison]', '=')
    .set('filters[status][value][]', 'ok')
}}

Status Values: - ok - OK Beans - partial_data - Partial Data - invalid_data - Invalid Data - extraction_failed - Extraction Failed - discarded - Discarded

Additional Filters: - Availability: filters[available][value][] = 'true' or 'false' - Origin Type: filters[originType][value][] = 'Single Origin' or 'Blend'

4. Update Menu in DashboardController

File: src/Controller/Admin/DashboardController.php

In configureMenuItems(), add:

yield MenuItem::section('Coffee Bean Management');
yield MenuItem::linkToRoute('Review Dashboard', 'fa fa-clipboard-list', 'admin_review_dashboard');
yield MenuItem::linkToCrud('Coffee Beans', 'fa fa-coffee', CoffeeBean::class);
// ... rest of menu items

Expected Result

Users will see a dashboard page with: - Status Cards: Visual cards showing count for each status with "View All" buttons - Quick Filters: Pre-configured filter links for common use cases (Available beans, Single Origin, etc.) - Direct Navigation: All links go to the main CoffeeBeanCrudController with filters pre-applied

This gives the team "prefilled filters" without creating duplicate CRUD interfaces.

Technical Notes

Filter Syntax Reference

  • ChoiceFilter: filters[fieldName][comparison] = '=' and filters[fieldName][value][] = enum value
  • BooleanFilter: filters[fieldName][comparison] = '=' and filters[fieldName][value][] = 'true'/'false'
  • EntityFilter: filters[fieldName][comparison] = '=' and filters[fieldName][value][] = entity ID

AdminContext Requirement

Always ensure AdminContext exists when rendering EasyAdmin templates (see DashboardController pattern, required for FrankenPHP compatibility).

Twig Function

Use ea_url() in templates instead of PHP AdminUrlGenerator - more elegant and template-friendly.

Dependencies

  • Existing CoffeeBeanCrudController with all its filters
  • Existing DashboardController structure
  • EasyAdmin layout templates
  • Bootstrap CSS (already in use)

Follow-up Possibilities

  • Add more filter combinations as the team identifies common workflows
  • Add date range filters (e.g., "Beans added this week")
  • Add roaster-based filter cards
  • Add search functionality directly on the dashboard