diff --git a/EXPLORE_AUTO_LOCATION_UPDATE.md b/EXPLORE_AUTO_LOCATION_UPDATE.md new file mode 100644 index 0000000..c2bceb2 --- /dev/null +++ b/EXPLORE_AUTO_LOCATION_UPDATE.md @@ -0,0 +1,89 @@ +# Explore Page - Automatic Location Tracking Implementation + +## Summary +Successfully removed the "Use My Location" button and implemented automatic location tracking on the explore page (http://127.0.0.1:8000/explore). + +## Changes Made + +### File Modified +- `resources/views/clubs/explore.blade.php` + +### Key Changes + +#### 1. UI Changes +- **Removed**: "Use My Location" button from the page header +- **Removed**: Success alert messages for location detection +- **Added**: Map card footer displaying current coordinates (Latitude & Longitude) +- **Updated**: Alert box now only shows for errors (hidden by default) + +#### 2. JavaScript Functionality Changes + +##### Added Variables +- `watchId`: Stores the geolocation watch ID for continuous tracking +- `isFirstLocation`: Flag to differentiate between initial location detection and updates + +##### Modified Functions +- **Removed**: `getUserLocation()` function (no longer needed) +- **Added**: `startWatchingLocation()` function that uses `navigator.geolocation.watchPosition()` instead of `getCurrentPosition()` +- **Added**: `updateUserLocation(lat, lng)` function to update the user marker position on the map when location changes + +##### Automatic Location Tracking +- Location tracking starts automatically when the page loads +- Uses `watchPosition()` API to continuously monitor location changes +- Only updates the map and fetches new clubs when location changes significantly (>100 meters / 0.001 degrees) +- First location detection initializes the map and fetches nearby clubs +- Subsequent location changes update the marker position and refresh nearby clubs + +#### 3. Behavior Changes + +**Before:** +- User had to manually click "Use My Location" button +- Location was fetched only once per button click +- Required user interaction to update location + +**After:** +- Location is automatically detected when page loads +- Location is continuously monitored in the background +- Map and clubs list automatically update when user moves +- No user interaction required + +## Technical Details + +### Geolocation API Configuration +```javascript +{ + enableHighAccuracy: true, // Use GPS for better accuracy + timeout: 10000, // 10 second timeout + maximumAge: 0 // Don't use cached positions +} +``` + +### Location Change Threshold +- Updates trigger when location changes by more than 0.001 degrees (~100 meters) +- Prevents excessive API calls for minor GPS fluctuations + +### Error Handling +- Maintains existing error handling for: + - Permission denied + - Position unavailable + - Timeout errors + - Unsupported browser + +## Testing Recommendations + +1. **Initial Load**: Verify location is automatically requested on page load +2. **Permission Prompt**: Confirm browser asks for location permission +3. **Map Display**: Check that map initializes with user's location +4. **Clubs Display**: Verify nearby clubs are fetched and displayed +5. **Location Updates**: Test that moving location updates the map (may require mobile device or location spoofing) +6. **Error Handling**: Test with location permissions denied + +## Browser Compatibility +- Works with all modern browsers that support Geolocation API +- Requires HTTPS in production (browsers restrict geolocation on HTTP) +- localhost/127.0.0.1 works without HTTPS for development + +## Notes +- The `watchPosition()` API continuously monitors location, which may impact battery life on mobile devices +- Location updates are throttled to only trigger when movement exceeds ~100 meters +- Users can still deny location permission, in which case appropriate error messages are shown diff --git a/FAMILY_EDIT_UPDATES.md b/FAMILY_EDIT_UPDATES.md new file mode 100644 index 0000000..a6880c5 --- /dev/null +++ b/FAMILY_EDIT_UPDATES.md @@ -0,0 +1,120 @@ +# Family Member Edit Form Updates + +## Summary +Updated the family member edit form to match the guardian profile edit page by adding missing fields. + +## Changes Made + +### 1. Updated View: `resources/views/family/edit.blade.php` +Added the following sections: +- ✅ **Profile Picture Upload Section** + - Displays current profile picture or default avatar + - Button to open upload modal + - Integrated with image upload modal component + +- ✅ **Mobile Number Field** + - Text input for mobile number + - Optional field with validation + +- ✅ **Social Media Links Section** + - Dynamic add/remove functionality + - Support for 22 social platforms (Facebook, Twitter, Instagram, LinkedIn, YouTube, TikTok, etc.) + - Platform dropdown and URL input for each link + - JavaScript functionality to add/remove links dynamically + +- ✅ **Personal Motto Field** + - Textarea for personal motto/quote + - Maximum 500 characters + - Helper text included + +### 2. Updated Controller: `app/Http/Controllers/FamilyController.php` + +#### Updated `update()` method: +- Added validation for new fields: + - `mobile` (nullable, max 20 characters) + - `social_links` (array with platform and url validation) + - `motto` (nullable, max 500 characters) +- Added social links processing logic to convert array format to associative array +- Updated dependent user update to include all new fields + +#### Added `uploadFamilyMemberPicture()` method: +- Handles profile picture uploads for family members +- Validates image file (jpeg, png, jpg, gif, max 5MB) +- Verifies family member belongs to authenticated user +- Generates unique filename +- Stores in `public/images/profiles` +- Deletes old profile picture if exists +- Returns JSON response for AJAX handling + +### 3. Updated Routes: `routes/web.php` +- ✅ Added route: `POST /family/{id}/upload-picture` → `family.upload-picture` + +## Features Now Available + +The family member edit form now includes all the same fields as the guardian profile edit page: + +1. **Profile Picture** - Upload and manage profile photos +2. **Full Name** - Required field +3. **Email Address** - Optional for children +4. **Mobile Number** - Optional contact number +5. **Gender** - Male/Female selection +6. **Birthdate** - Date picker +7. **Blood Type** - Dropdown with all blood types +8. **Nationality** - Country dropdown component +9. **Social Media Links** - Dynamic list with 22 platform options +10. **Personal Motto** - Text area for inspirational quotes +11. **Relationship Type** - Son/Daughter/Spouse/Sponsor/Other +12. **Is Billing Contact** - Checkbox + +## Technical Details + +### Social Links Processing +- Frontend: Array of objects `[{platform: 'facebook', url: 'https://...'}]` +- Backend: Converted to associative array `{'facebook': 'https://...'}` +- Stored in database as JSON + +### Profile Picture Upload +- Uses existing `x-image-upload-modal` component +- AJAX upload with cropping functionality +- Aspect ratio: 1:1 (square) +- Max size: 1MB (as per modal config) +- Stored in: `storage/app/public/images/profiles/` + +## Testing Checklist + +- [ ] Profile picture upload works for family members +- [ ] Mobile number saves correctly +- [ ] Social links can be added dynamically +- [ ] Social links can be removed +- [ ] Social links save correctly +- [ ] Personal motto saves correctly +- [ ] All existing fields still work (name, email, gender, etc.) +- [ ] Form validation works properly +- [ ] Success message displays after update +- [ ] Redirect to family dashboard works + +## Files Modified + +1. `resources/views/family/edit.blade.php` - Added new form fields and JavaScript +2. `app/Http/Controllers/FamilyController.php` - Updated validation and added upload method +3. `routes/web.php` - Added profile picture upload route + +## Notes + +### Default Avatar Image +The code references `asset('images/default-avatar.png')` which doesn't currently exist in `public/images/`. + +**Options:** +1. Create a default avatar image at `public/images/default-avatar.png` +2. Use a placeholder service like `https://ui-avatars.com/api/?name=User&size=120` +3. Use the same approach as the show page (gradient background with initials) + +**Current behavior:** If the file doesn't exist, the browser will show a broken image icon until a profile picture is uploaded. + +### Storage Directory +Make sure the storage directory is linked: +```bash +php artisan storage:link +``` + +This creates a symbolic link from `public/storage` to `storage/app/public` so uploaded images are accessible. diff --git a/app/Http/Controllers/FamilyController.php b/app/Http/Controllers/FamilyController.php index b15dbc6..23eb643 100644 --- a/app/Http/Controllers/FamilyController.php +++ b/app/Http/Controllers/FamilyController.php @@ -236,10 +236,15 @@ class FamilyController extends Controller $validated = $request->validate([ 'full_name' => 'required|string|max:255', 'email' => 'nullable|email|max:255', + 'mobile' => 'nullable|string|max:20', 'gender' => 'required|in:m,f', 'birthdate' => 'required|date', 'blood_type' => 'nullable|string|max:10', 'nationality' => 'required|string|max:100', + 'social_links' => 'nullable|array', + 'social_links.*.platform' => 'required_with:social_links.*.url|string', + 'social_links.*.url' => 'required_with:social_links.*.platform|url', + 'motto' => 'nullable|string|max:500', 'relationship_type' => 'required|string|max:50', 'is_billing_contact' => 'boolean', ]); @@ -249,14 +254,27 @@ class FamilyController extends Controller ->where('dependent_user_id', $id) ->firstOrFail(); + // Process social links - convert from array of objects to associative array + $socialLinks = []; + if (isset($validated['social_links']) && is_array($validated['social_links'])) { + foreach ($validated['social_links'] as $link) { + if (!empty($link['platform']) && !empty($link['url'])) { + $socialLinks[$link['platform']] = $link['url']; + } + } + } + $dependent = User::findOrFail($id); $dependent->update([ 'full_name' => $validated['full_name'], 'email' => $validated['email'], + 'mobile' => $validated['mobile'], 'gender' => $validated['gender'], 'birthdate' => $validated['birthdate'], 'blood_type' => $validated['blood_type'], 'nationality' => $validated['nationality'], + 'social_links' => $socialLinks, + 'motto' => $validated['motto'], ]); $relationship->update([ @@ -268,6 +286,58 @@ class FamilyController extends Controller ->with('success', 'Family member updated successfully.'); } + /** + * Upload profile picture for a family member. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function uploadFamilyMemberPicture(Request $request, $id) + { + $request->validate([ + 'image' => 'required|image|mimes:jpeg,png,jpg,gif|max:5120', // 5MB max + ]); + + $user = Auth::user(); + + // Verify the family member belongs to the authenticated user + $relationship = UserRelationship::where('guardian_user_id', $user->id) + ->where('dependent_user_id', $id) + ->firstOrFail(); + + $familyMember = User::findOrFail($id); + + if ($request->hasFile('image')) { + $image = $request->file('image'); + + // Generate unique filename + $filename = 'profile_' . $familyMember->id . '_' . time() . '.' . $image->getClientOriginalExtension(); + + // Store in public/images/profiles + $path = $image->storeAs('images/profiles', $filename, 'public'); + + // Delete old profile picture if exists + if ($familyMember->profile_picture && \Storage::disk('public')->exists($familyMember->profile_picture)) { + \Storage::disk('public')->delete($familyMember->profile_picture); + } + + // Update family member + $familyMember->update(['profile_picture' => $path]); + + return response()->json([ + 'success' => true, + 'message' => 'Profile picture uploaded successfully.', + 'path' => $path, + ]); + } + + return response()->json([ + 'success' => false, + 'message' => 'No image file provided.', + ], 400); + } + /** * Remove the specified family member from storage. * diff --git a/resources/views/clubs/explore.blade.php b/resources/views/clubs/explore.blade.php index 7f7d3e8..fee33db 100644 --- a/resources/views/clubs/explore.blade.php +++ b/resources/views/clubs/explore.blade.php @@ -2,68 +2,123 @@ @section('content')
Discover what's near you
-Discover sports clubs, trainers, nutrition clinics, and more near you
+Detecting location...
+