theabhishekin / filament-location
A Filament package for collecting and displaying user locations with Google Maps integration
Installs: 6
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/theabhishekin/filament-location
Requires
- php: ^8.1
- filament/filament: ^3.0
- illuminate/contracts: ^10.0|^11.0
Requires (Dev)
- laravel/pint: ^1.0
- nunomaduro/collision: ^7.0|^8.0
- orchestra/testbench: ^8.0|^9.0
- pestphp/pest: ^2.0
- pestphp/pest-plugin-arch: ^2.0
- pestphp/pest-plugin-laravel: ^2.0
- phpstan/extension-installer: ^1.3
- phpstan/phpstan: ^1.10
- phpstan/phpstan-deprecation-rules: ^1.1
- phpstan/phpstan-phpunit: ^1.3
README
A powerful Filament package for collecting and displaying user locations with Google Maps integration. This package provides easy-to-use location picker and display components for your Filament admin panels.
Features
- πΊοΈ LocationPicker: Interactive Google Maps component for forms
- π LocationColumn: Display locations in tables with clickable map modals
- π― Current Location: Get user's current location with GPS
- π§ Highly Customizable: Configurable zoom, map types, controls, and styling
- π± Responsive Design: Works perfectly on desktop and mobile
- π Multiple Location Types: Support for home, office, check-in/out locations
- π Distance Calculations: Built-in distance calculation utilities
- π¨ Custom Icons: Predefined and custom icon support
Installation
You can install the package via Composer:
composer require theabhishekin/filament-location
Publish the configuration file:
php artisan vendor:publish --tag="filament-location-config"
Add your Google Maps API key to your .env file:
GOOGLE_MAPS_API_KEY=your_google_maps_api_key_here
Note: Make sure to enable the Maps JavaScript API and Geocoding API in your Google Cloud Console.
Configuration
The configuration file allows you to customize default settings:
return [ 'google_maps_api_key' => env('GOOGLE_MAPS_API_KEY'), 'default_zoom' => 15, 'map_height' => '400px', 'enable_street_view' => true, 'map_type' => 'standard', // standard, satellite, hybrid, terrain 'location_accuracy' => 100, 'location_timeout' => 10000, 'enable_high_accuracy' => true, 'map_controls' => [ 'zoom_control' => true, 'map_type_control' => true, 'scale_control' => true, 'street_view_control' => true, 'rotate_control' => true, 'fullscreen_control' => true, ], ];
Database Migration
Add location fields to your model's migration:
Schema::table('users', function (Blueprint $table) { $table->json('location')->nullable(); $table->timestamp('location_updated_at')->nullable(); });
Model Setup
Add the HasLocation trait to your model:
<?php namespace App\Models; use TheAbhishekIN\FilamentLocation\Concerns\HasLocation; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use HasLocation; protected $fillable = [ 'name', 'email', 'location', 'location_updated_at', ]; protected $casts = [ 'location' => 'array', 'location_updated_at' => 'datetime', ]; }
Quick Example: User Resource
Here's a complete example of how to use the location components in a Filament resource:
<?php namespace App\Filament\Resources; use App\Models\User; use TheAbhishekIN\FilamentLocation\Forms\Components\LocationPicker; use TheAbhishekIN\FilamentLocation\Tables\Columns\LocationColumn; use Filament\Forms; use Filament\Forms\Form; use Filament\Resources\Resource; use Filament\Tables; use Filament\Tables\Table; class UserResource extends Resource { protected static ?string $model = User::class; protected static ?string $navigationIcon = 'heroicon-o-users'; public static function form(Form $form): Form { return $form ->schema([ Forms\Components\TextInput::make('name') ->required() ->maxLength(255), Forms\Components\TextInput::make('email') ->email() ->required() ->unique(ignoreRecord: true) ->maxLength(255), LocationPicker::make('location') ->label('User Location') ->required() ->zoom(16) ->height('400px') ->mapType('standard') ->showCoordinates(true) ->helperText('Click on the map or use "Get Current Location" to set the location'), Forms\Components\DateTimePicker::make('location_updated_at') ->label('Location Updated At') ->disabled(), ]); } public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('name') ->searchable() ->sortable(), Tables\Columns\TextColumn::make('email') ->searchable() ->sortable(), LocationColumn::make('location') ->label('Location') ->iconType('home') ->iconSize('lg') ->iconColor('primary') ->title('User Location') ->showTooltip(true) ->tooltipText('Click to view location on map') ->getLatitudeUsing(fn($record) => $record->location['latitude'] ?? null) ->getLongitudeUsing(fn($record) => $record->location['longitude'] ?? null), Tables\Columns\TextColumn::make('location_updated_at') ->label('Location Updated') ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ Tables\Filters\Filter::make('has_location') ->label('Has Location') ->query(fn($query) => $query->whereNotNull('location')) ->toggle(), ]) ->actions([ Tables\Actions\EditAction::make(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), ]); } public static function getPages(): array { return [ 'index' => Pages\ListUsers::route('/'), 'create' => Pages\CreateUser::route('/create'), 'edit' => Pages\EditUser::route('/{record}/edit'), ]; } }
Usage
LocationPicker Component
LocationPicker::make('location') ->label('Select Location') ->required() ->zoom(15) // Map zoom level (1-20) ->height('400px') // Map container height ->mapType('standard') // standard, satellite, hybrid, terrain ->showCoordinates(true) // Show latitude/longitude coordinates ->showMap(true) // Show/hide the map ->mapControls([ // Configure map controls 'zoom_control' => true, 'street_view_control' => false, ]) ->helperText('Click on the map to select a location');
LocationColumn Component
LocationColumn::make('location') ->label('Location') ->iconType('home') // home, office, check-in, check-out, travel ->iconSize('lg') // sm, md, lg, xl ->iconColor('primary') // primary, success, warning, danger ->title('Location Details') // Modal title ->showTooltip(true) // Show hover tooltip ->tooltipText('View on map') // Custom tooltip text ->getLatitudeUsing(fn($record) => $record->location['latitude'] ?? null) ->getLongitudeUsing(fn($record) => $record->location['longitude'] ?? null);
Available Icon Types
| Icon Type | Description | Default Color |
|---|---|---|
home |
Home locations | Blue |
office |
Office locations | Purple |
check-in |
Check-in locations | Green |
check-out |
Check-out locations | Orange |
travel |
Travel locations | Red |
Use Cases
- Employee Management: Track employee locations for check-in/check-out
- Customer Management: Store customer addresses and locations
- Venue Management: Manage multiple business locations
- Event Management: Record event locations and venue details
- Delivery Services: Track delivery addresses and routes
- Real Estate: Manage property locations
- Healthcare: Patient and facility location management
Distance Calculations
The HasLocation trait provides distance calculation utilities:
// Calculate distance between two points $distance = $user->distanceTo(26.9124, 75.7873); // Returns distance in kilometers // Calculate distance to another model $distance = $user->distanceToModel($otherUser); // Find users within 10km radius $nearbyUsers = User::withinDistance(26.9124, 75.7873, 10)->get(); // Order users by distance from a point $usersByDistance = User::orderByDistance(26.9124, 75.7873)->get();
Security & HTTPS Requirements
β οΈ Important Security Notice:
The browser's Geolocation API requires a secure context to function properly. This means:
- β HTTPS connections (recommended for production)
- β localhost (for development)
- β HTTP connections will be blocked by modern browsers
Error Handling
The package provides comprehensive error handling with user-friendly messages:
| Error Type | Message | Description |
|---|---|---|
| HTTPS Required | π Location services require a secure connection (HTTPS) | Browser blocks geolocation on non-secure origins |
| Permission Denied | π« Location access denied. Please allow location permission | User explicitly denied location access |
| Position Unavailable | π Location information is unavailable | Device can't determine location |
| Timeout | β±οΈ Location request timed out | Location request exceeded timeout limit |
All errors are displayed in a prominent red banner below the location button with:
- π¨ Enhanced styling with gradient background
- π± Responsive design for mobile devices
- β¨ Smooth slide-in animation
- π Clear iconography and user-friendly messages
Development Setup
For local development, ensure you're using:
# Use localhost (secure context) http://localhost:8000 # Or use Herd/Valet with HTTPS https://your-app.test
Troubleshooting
Common Issues
1. Livewire Entanglement Error
Error: Livewire property ['data.location'] cannot be found
Solution: Ensure your Livewire component has the property that matches your field name:
// β Wrong - Component doesn't have 'location' property class OrdersStep extends Component { public $data = []; // Missing 'location' key } // β Correct - Component has the location property class OrdersStep extends Component { public $location = null; // Direct property // OR public $data = [ 'location' => null, // Nested property ]; }
2. HTTPS/Security Errors
Error: Only secure origins are allowed
Solutions:
- Use HTTPS in production:
https://yourdomain.com - Use localhost for development:
http://localhost:8000 - Use Valet/Herd with SSL:
https://yourapp.test
3. Error Messages Not Showing
If error messages aren't displaying:
- Check console logs for JavaScript errors
- Verify Alpine.js is loaded before the LocationPicker
- Ensure CSS is published:
php artisan vendor:publish --tag=filament-location-assets
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.