Admin Indicator Submissions
Intro
The Admin Indicator Submissions feature provides a comprehensive workflow for managing indicator task submissions within the Filament admin panel. This feature allows administrators to submit their assigned indicator tasks with supporting documentation, values, and comments through an intuitive interface.
The system supports both Success Indicators and Compliance Indicators, with conditional file upload requirements and a robust event-driven workflow that ensures proper task progression and status management.
Note: For details on how submissions are verified after submission, see Indicator Verification.
Architecture Overview
The Admin Indicator Submissions feature is built as a Filament Resource within the Indicators cluster, providing administrators with a dedicated interface for completing their assigned indicator tasks.
Core Models
- IndicatorTask: Represents tasks assigned to administrators for indicator completion
- IndicatorSubmission: Contains the actual submission data (values, files, comments)
Event-Driven Workflow
The system uses Laravel events to manage the submission workflow:
IndicatorSubmissionSubmitted→ Triggers verification workflow (see Indicator Verification)IndicatorSubmissionRejected→ Returns task to submitter with feedback for revision
Workflow Process
1. Task Assignment
- Administrators are assigned IndicatorTasks through existing mechanisms
- Tasks appear in the Indicators cluster when users have assigned tasks
- Tasks are scoped by user (not tenant) for proper visibility control
2. Submission Process
- Admin opens submission modal from their task list
- Form dynamically populates based on indicator type and requirements
- File uploads are conditionally required based on
supporting_documentationfield - Comments and values are captured along with file attachments
- Submission triggers
IndicatorSubmissionSubmittedevent
3. Status Management
- Tasks progress through: Pending → Submitted → In Verification → Complete
- Status display uses separate
displayLabel()for UI anddisplayStatus()for logic - Failed submissions return to "Needs Revision" status with previous data pre-filled
Implementation Details
Filament Cluster Structure
// app/Filament/Admin/Clusters/Indicators.php
class Indicators extends Cluster
{
protected static ?string $navigationGroup = 'Manage';
public static function canAccess(): bool
{
return static::shouldRegisterNavigation();
}
public static function shouldRegisterNavigation(): bool
{
$user = auth()->user();
return $user->indicatorTasks()->exists() ||
$user->indicatorReviewTasks()->exists();
}
}
User-Scoped Queries
Unlike most Filament resources, Indicators use user-based scoping instead of tenant scoping:
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()
->where('responsible_user_id', auth()->id())
->with(['indicator', 'entrepreneur', 'organisation', 'programme']);
}
Service Layer Integration
The IndicatorSubmissionService was extended with Filament adapter methods:
// Service adapter for Filament compatibility
public function createSubmissionFromFilament(array $data, IndicatorTask $task): IndicatorSubmission
{
$transformedData = $this->transformFilamentData($data, $task);
return $this->createSubmission($transformedData, $task);
}
private function transformFilamentData(array $data, IndicatorTask $task): array
{
// Transforms Filament form data to service-expected format
// Handles file upload path conversions
// Maintains backward compatibility
}
File Upload Handling
File uploads are converted from Filament's storage paths to proper UploadedFile instances:
private function transformFilamentAttachments(?array $attachments): array
{
if (empty($attachments)) return [];
return collect($attachments)->map(function ($path) {
// Handle various Filament path formats
$storagePath = str_replace(['storage/app/public/', 'public/'], '', $path);
return new UploadedFile(
storage_path("app/public/{$storagePath}"),
basename($storagePath),
Storage::disk('public')->mimeType($storagePath),
null,
true
);
})->toArray();
}
Event Listeners Integration
Event listeners handle workflow progression after submission:
// When a submission is created, it triggers the IndicatorSubmissionSubmitted event
public function createSubmissionFromFilament(array $data, IndicatorTask $task): IndicatorSubmission
{
$transformedData = $this->transformFilamentData($data, $task);
$submission = $this->createSubmission($transformedData, $task);
// Event is dispatched automatically by the service
event(new IndicatorSubmissionSubmitted($submission));
return $submission;
}
The IndicatorSubmissionSubmitted event automatically initiates the verification workflow if required. If no verification is needed, the task is marked as complete immediately. For details on how verification is handled, see Indicator Verification.
User Interface
Tabbed Interface
The Admin Submissions resource uses tabbed interfaces for organization:
- Pending: Shows Pending, Needs Revision, and Overdue tasks
- In Verification: Shows Submitted tasks
- Complete: Shows Completed tasks
Dynamic Modal System
Modals adapt based on task status:
- Submit Mode: Interactive form for pending/revision tasks with full edit capabilities
- View Mode: Read-only display for submitted/complete tasks showing submission details
Form Field Behavior
Forms are dynamically generated based on indicator data:
- Title from
indicator.title - Helper text from
additional_instruction - File uploads conditional on
supporting_documentation - Pre-filled data for revision tasks
Permission and Access Control
Cluster Visibility
The Indicators cluster only appears for users with relevant tasks:
public static function shouldRegisterNavigation(): bool
{
$user = auth()->user();
return $user->indicatorTasks()->exists() ||
$user->indicatorReviewTasks()->exists();
}
Filament Panel Requirements
App Panel Access requires:
- User must be admin:
$user->isAdmin() - Current tenant must be landlord:
$tenant->isLandlord() - OR user has
MANAGE_SESSIONS_SCHEDULERpermission
Admin Panel Access requires:
- User has
VIEW_ADMIN_DASHBOARDpermission - Admin role assignment
Data Scoping
- User-scoped: Only assigned tasks visible to each user
- Non-tenant scoped: Works across tenant boundaries
- Relationship eager loading: Prevents N+1 queries
Testing Considerations
Unit Test Coverage
The feature includes comprehensive test coverage:
- Service Tests:
IndicatorSubmissionServiceTest.php - Filament Resource Tests:
IndicatorsClusterTest.php,IndicatorSubmissionsResourceTest.php
Note: For verification-related test coverage, see Indicator Verification.
Parallel Test Isolation
Key considerations for parallel test execution:
- Tenant Context: Tests require proper landlord tenant setup
- Role/Permission Creation: Use
firstOrCreate()to prevent conflicts - Database Constraints: Handle foreign key and unique constraints properly
- Event Dispatching: Remove
ShouldDispatchAfterCommitfor immediate testing
Test Setup Pattern
beforeEach(function () {
// Create tenant context for Filament panel access
$this->tenant = Tenant::firstOrCreate(
['id' => config('multitenancy.landlord_id')],
['id' => config('multitenancy.landlord_id'), 'name' => 'Landlord Tenant']
);
app()->instance('currentTenant', $this->tenant);
// Create admin with proper permissions
$this->admin = User::factory()->create();
$adminRole = Role::firstOrCreate(['name' => 'admin'], [...]);
$this->admin->roles()->attach($adminRole, ['tenant_id' => config('multitenancy.landlord_id')]);
$this->actingAs($this->admin);
});
Performance Considerations
Query Optimization
- Eager loading relationships:
->with(['indicator', 'entrepreneur', 'organisation']) - User-scoped queries prevent large dataset issues
- Pagination implemented for table views
File Storage
- Production: Files stored in S3 via
indicator_submissionsdisk (s3://bucket/indicator_submissions/) - Testing: Files stored locally via
indicator_submissions_localdisk (storage/app/public/indicator_submissions/) - Proper MIME type detection and validation
- Secure file access through Laravel's storage system
Caching
- No explicit caching implemented (relies on Laravel's query cache)
- Consider adding model-level caching for frequently accessed data
Maintenance Notes
Important Considerations for Future Developers
-
Service Layer: The
IndicatorSubmissionServicemaintains dual compatibility - both controller and Filament usage. Don't break this pattern. -
Event System: Events are crucial for workflow progression. Always test event dispatching when making changes.
-
Status Management: The dual-status system (
displayLabelvsdisplayStatus) serves different purposes - don't merge them. -
File Handling: Filament file uploads require transformation to
UploadedFileobjects. This is handled in the service adapter. -
User Scoping: This feature intentionally uses user-based scoping instead of tenant scoping. Don't convert to tenant scoping.
Common Pitfalls
- Missing Tenant Context: Filament panels require proper tenant setup in tests
- Event Timing: Be careful with event dispatching timing - some tests need immediate dispatch
- File Path Handling: Filament provides various path formats that need normalization
- Permission Stacking: Admin users need both role assignment AND specific permissions
Extension Points
The feature is designed for extension:
- Additional indicator types can be added easily
- Verification levels can be increased beyond 2
- Additional file types and validation rules can be implemented
- Custom business logic can be added to event listeners
Configuration
Required Configuration
// config/multitenancy.php
'landlord_id' => 1,
// Required roles (created via seeder or migration)
- 'admin' role with appropriate permissions
// Required permissions
- VIEW_ADMIN_DASHBOARD
- MANAGE_SESSIONS_SCHEDULER (for app panel alternative access)
// File storage disks (see Storage Configuration section)
- 'indicator_submissions'
Note: For verifier role configuration, see Indicator Verification.
Storage Configuration
// config/filesystems.php
// Production S3 storage (default)
'indicator_submissions' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'visibility' => 'public',
'root' => 'indicator_submissions/',
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
],
Bulk Import Feature
Overview
Allows administrators to submit multiple indicator tasks at once via CSV upload. Only works for indicators that don't require supporting documentation.
How to Use
- Generate Template: Select tasks in the table → Click "Generate CSV Template" bulk action → Download pre-populated CSV
- Fill Values: Complete the
valueandcommentcolumns following the format instructions in the CSV - Import: Click "Import Submissions" → Upload CSV → Review results
Value Formats
- Boolean:
1,0,true,false(case-insensitive) - Percentage: Number between 0-100
- Monetary: Number ≥ 0
- Numeric: Any number
Key Limitations
- No attachments - Indicators requiring supporting documentation cannot be bulk imported
- User scoped - Can only import for your own assigned tasks
- CSV only - Excel files not supported
- Creates new submissions - Doesn't update existing ones
Important Notes
Permission Required: bulk-submit-indicators
Files:
- Importer:
app/Filament/Imports/IndicatorSubmissionImporter.php - Export:
app/Exports/IndicatorSubmissionTemplateExport.php - Tests:
tests/Feature/IndicatorBulkImportTest.php
Integration: Uses IndicatorSubmissionService::createSubmissionFromFilament() so all events, verification workflows, and status management work identically to manual submissions.
Common Issues
Import succeeds but no records created:
resolveRecord()must return the createdIndicatorSubmissionmodel, notnull- Filament uses the return value to track successful imports
Template generation fails with documentation error:
- Some selected indicators require supporting documentation
- Unselect those indicators or submit them manually with attachments
This comprehensive documentation should provide future developers with everything needed to understand, maintain, and extend the Admin Indicator Submissions feature, including the bulk import functionality. For verification workflow details, see Indicator Verification.