Strip Filters
Strip Admin Filter System Documentation
Overview
The Strip Admin filter system provides a unified, reusable filtering infrastructure for list pages (users, characters, media, etc.). This architecture eliminates code duplication while maintaining flexibility and extensibility.
Architecture Components
1. Filter Parser (parser.go)
The parser module handles extracting and validating filter parameters from HTTP requests.
Key Functions:
ParseCommonFilters()- Extracts standard parameters (search, cursor, limit, sort)ParseDateRange()- Handles date range filters with automatic time adjustmentsParseNumberRange()- Parses numeric range filters with validationParseBooleanFilter()- Manages tri-state boolean filters (true/false/unset)ParseEnumFilter()- Extracts enum values, ignoring defaults
Example Usage:
// In a handlercommonFilters := filters.ParseCommonFilters(c, 25) // default limit: 25createdRange := filters.ParseDateRange(c, "created")hasMedia := filters.ParseBooleanFilter(c, "has_media")2. Filter Builder (builder.go)
The builder module provides configuration and UI generation for filters.
Key Types:
FilterConfig- Complete filter configuration for a pageFilterDefinition- Individual filter metadataFilterValues- Current filter values map
Key Functions:
NewUsersFilterConfig()- Returns filter configuration for users pageNewCharactersFilterConfig()- Returns configuration for charactersNewMediaFilterConfig()- Returns configuration for mediaBuildFilterDefinitions()- Converts configs to template-compatible filtersCountActiveFilters()- Counts currently active filters
Example Usage:
config := filters.NewUsersFilterConfig()values := filters.FilterValues{ "status": "active", "created": dateRange,}templateFilters := filters.BuildFilterDefinitions(config, values)3. Storage Filters (storage/filters.go)
Provides reusable database query builders for common filter operations.
Key Functions:
ApplyDateRangeFilter()- Adds date range conditions to queriesApplyNumberRangeFilter()- Adds numeric range conditionsApplySearchFilter()- Implements multi-column searchApplyBooleanFilter()- Handles boolean conditionsApplyEnumFilter()- Filters by enum valuesParseCommonFilters()- Extracts cursor, limit, search, and sort parameters
Example Usage:
query := db.NewSelect().Model(&users)query = storage.ApplyDateRangeFilter(query, "created_at", from, to)query = storage.ApplySearchFilter(query, searchTerm, "email", "name", "id")query = query.Limit(int(commonFilters.Limit))Filter Types
1. Date Range Filters
- Format:
{field}_fromand{field}_to - Automatically handles end-of-day adjustment for
todates - Supports both YYYY-MM-DD and RFC3339 formats
2. Number Range Filters
- Format:
{field}_minand{field}_max - Validates non-negative integers
- Supports int32 range
3. Boolean Filters
- Format:
has_{field}oris_{field} - Tri-state: true, false, or unset (no filter)
- Uses separate
IsActiveflag to distinguish false from unset
4. Enum/Select Filters
- Format:
{field}_filter - Ignores default values (empty string, “0”)
- Maps display values to database values
5. Text Search
- Standard
searchparameter - Searches across multiple columns
- Case-insensitive with pattern matching
Adding Filters to a New Page
Step 1: Define Filter Configuration
Create a new filter configuration function in builder.go:
func NewMyPageFilterConfig() FilterConfig { return FilterConfig{ TargetURL: "/mypage", TargetID: "mypage-table-container", Definitions: []FilterDefinition{ { ID: "status", Label: "Status", Type: FilterTypeSelect, Placeholder: "All Statuses", Options: []FilterOption{ {Value: "active", Label: "Active"}, {Value: "inactive", Label: "Inactive"}, }, }, { ID: "created", Label: "Created Date", Type: FilterTypeDateRange, }, }, }}Step 2: Update Handler
func (h *Handlers) HandleMyPage(c *fiber.Ctx) error { // Parse filters commonFilters := filters.ParseCommonFilters(c, 25) statusFilter := filters.ParseEnumFilter(c, "status") createdRange := filters.ParseDateRange(c, "created")
// Build request req := &MyPageRequest{ Cursor: commonFilters.Cursor, Limit: commonFilters.Limit, Search: commonFilters.Search, Status: statusFilter, CreatedFrom: createdRange.From, CreatedTo: createdRange.To, }
// ... rest of handler logic}Step 3: Update Storage Query
func (s *Storage) ListMyItems(ctx context.Context, filters *MyFilters) ([]*Item, error) { query := s.db.NewSelect().Model(&items)
// Apply filters query = ApplySearchFilter(query, filters.Search, "name", "description") query = ApplyEnumFilter(query, "status", filters.Status) query = ApplyDateRangeFilter(query, "created_at", filters.CreatedFrom, filters.CreatedTo) query = query.Limit(int(filters.Limit))
// Execute query var results []*Item err := query.Scan(ctx, &results) return results, err}Step 4: Use in Template
The existing filter components in templates/components/filters work automatically with the configuration.
Naming Conventions
Consistent naming ensures filters work correctly across the system:
- Date ranges:
{field}_from,{field}_to - Number ranges:
{field}_min,{field}_max - Boolean filters:
has_{field},is_{field} - Enum filters:
{field}_filteror just{field} - Common parameters:
search,cursor,limit,sort_by,sort_direction
URL Parameter Examples
/users?search=john&status=2&created_from=2024-01-01&created_to=2024-12-31/characters?gender_filter=MALE&has_media=true&sort_by=name&sort_direction=asc/media?type_filter=MEDIA_TYPE_IMAGE&generation_mode_filter=edit&limit=50Benefits
- DRY Principle: Single implementation for common filter logic
- Type Safety: Strongly typed filter definitions and values
- Consistency: Uniform parameter naming and behavior
- Maintainability: Centralized filter logic easy to update
- Extensibility: Simple to add new filter types or pages
- Performance: Optimized query builders reduce database load
- Backward Compatibility: Maintains existing URL structures
Testing
The refactored filter system maintains backward compatibility with existing URLs and parameters. All existing filters continue to work without changes to frontend code or bookmarked URLs.
To test filters:
- Run
make lintto check for code issues - Run
make run-airto start with hot reload - Test each page’s filters through the UI
- Verify pagination works with active filters
- Confirm filter persistence across page refreshes