Move family controllers to their own namespace

This commit is contained in:
Eugene Burmakin 2025-10-04 20:48:44 +02:00
parent f898f3aab0
commit cfe319df9b
9 changed files with 8 additions and 452 deletions

View file

@ -1,444 +0,0 @@
# Family Plan Feature - Implementation Status
## ✅ Feature Complete - Ready for Production
All phases of the Family Plan feature have been successfully implemented and tested. The feature is production-ready for both self-hosted instances and future Dawarich Cloud integration.
### ✅ Phase 1: Database Foundation - COMPLETED
- 3 database tables: families, family_memberships, family_invitations
- All models with associations and validations
- Standard bigint primary keys for performance
- Comprehensive test coverage (68+ tests)
### ✅ Phase 2: Core Business Logic - COMPLETED
- 4 service classes: Create, Invite, AcceptInvitation, Leave
- LocationSharingService for map integration
- Email templates and FamilyMailer
- Pundit authorization policies
- Comprehensive error handling
### ✅ Phase 3: Controllers and Routes - COMPLETED
- FamiliesController, FamilyMembershipsController, FamilyInvitationsController
- Custom Users::SessionsController for invitation flow
- Public and authenticated routes
- Full authorization integration
### ✅ Phase 4: User Interface - COMPLETED
- Family dashboard with map integration
- Enhanced invitation landing page with benefits display
- Family creation and management views
- Dark mode support throughout
- Stimulus controllers for interactive elements
- Navigation integration
### ✅ Phase 5: Polish and Enhancements - COMPLETED
- Family member map visualization with auto-zoom
- Real-time tooltips showing "Last updated: [timestamp]"
- Detailed popups with email and coordinates
- Error handling and user feedback
- Feature gating for cloud vs self-hosted
- Email notification system
---
## Overview
The Family Plan feature allows Dawarich users to create family groups, invite members, and share their latest location data within the family. This feature enhances the social aspect of location tracking while maintaining strong privacy controls.
### Key Features
- Create and manage family groups
- Invite members via email
- Share latest location data within family
- Role-based permissions (owner/member)
- Privacy controls for location sharing
- Email notifications and in-app notifications
### Business Rules
- Maximum 5 family members per family (hardcoded constant)
- One family per user (must leave current family to join another)
- Family owners cannot delete their accounts
- Invitation tokens expire after 7 days
- Only latest position sharing (no historical data access)
- Free for self-hosted instances, paid feature for Dawarich Cloud
## Database Schema
### 1. Families Table
- `id` (bigint, primary key)
- `name` (string, max 50 chars, not null)
- `creator_id` (bigint, foreign key to users, not null)
- `created_at`, `updated_at` (datetime)
- **Constant**: MAX_MEMBERS = 5
### 2. Family Memberships Table
- `id` (bigint, primary key)
- `family_id` (bigint, foreign key to families, not null)
- `user_id` (bigint, foreign key to users, not null, unique - one family per user)
- `role` (integer enum: owner=0, member=1, not null, default: member)
- `created_at`, `updated_at` (datetime)
### 3. Family Invitations Table
- `id` (bigint, primary key)
- `family_id` (bigint, foreign key to families, not null)
- `email` (string, not null, validated format)
- `token` (string, not null, unique - secure invitation token)
- `expires_at` (datetime, not null - 7 days from creation)
- `invited_by_id` (bigint, foreign key to users, not null)
- `status` (integer enum: pending=0, accepted=1, expired=2, cancelled=3)
- `created_at`, `updated_at` (datetime)
### 4. User Model Extensions
Added associations and helper methods:
- `in_family?` - Check if user is in a family
- `family_owner?` - Check if user owns their family
- `can_delete_account?` - Prevents owners from deleting accounts with members
## Core Architecture
### Service Classes
All family operations use service objects for business logic:
**Families::Create**
- Creates family with automatic owner membership
- Validates user eligibility (not already in family)
- Sends notification to creator
**Families::Invite**
- Sends email invitations with secure tokens
- Validates family capacity (max 5 members)
- Prevents duplicate invitations
- Checks invitee isn't already in a family
**Families::AcceptInvitation**
- Validates invitation status and email match
- Creates member membership
- Updates invitation status
- Notifies both user and family owner
**Families::Leave**
- Removes user from family
- Prevents owners from leaving with active members
- Handles ownership transfer logic
- Cleans up family if last member leaves
**Families::LocationSharingService**
- Retrieves latest location for each family member
- Powers family map visualization
- Returns location data with timestamps
### Controllers
**FamiliesController** - Family CRUD operations and dashboard
**FamilyMembershipsController** - Member management
**FamilyInvitationsController** - Invitation creation and acceptance
**Users::SessionsController** - Custom login flow with invitation tokens
### Authorization
Three Pundit policies control access:
- **FamilyPolicy** - Family operations (create, update, destroy, invite)
- **FamilyMembershipPolicy** - Member management
- **FamilyInvitationPolicy** - Invitation management
### Email System
**FamilyMailer** sends invitation emails with:
- Personalized invitation message
- Family name and inviter information
- Benefits explanation
- Accept invitation link
- 7-day expiration notice
## Error Handling
Services return `true`/`false` and expose `error_message` for user-friendly feedback:
- All database operations use transactions with rollback
- Comprehensive validation with specific error messages
- Controllers display service error messages to users
- Common errors: capacity limits, duplicate invitations, permission issues
## Routes and Navigation
### Public Routes
- `GET /invitations/:id` - Public invitation landing page (no auth required)
### Authenticated Routes
**Family Management:**
- `GET /families` - Family index (redirects to user's family)
- `GET /families/new` - Create family form
- `POST /families` - Create family
- `GET /families/:id` - Family dashboard with map
- `GET /families/:id/edit` - Family settings
- `PATCH /families/:id` - Update family
- `DELETE /families/:id` - Delete family
- `DELETE /families/:id/leave` - Leave family
- `PATCH /families/:id/update_location_sharing` - Update location sharing preferences
**Family Invitations:**
- `GET /families/:family_id/invitations` - List invitations
- `POST /families/:family_id/invitations` - Create invitation
- `GET /families/:family_id/invitations/:id` - Show invitation
- `POST /families/:family_id/invitations/:id/accept` - Accept invitation
- `DELETE /families/:family_id/invitations/:id` - Cancel invitation
**Family Members:**
- `GET /families/:family_id/members` - List members
- `GET /families/:family_id/members/:id` - Show member
- `DELETE /families/:family_id/members/:id` - Remove member
**API Endpoints:**
- `GET /api/v1/families/locations` - Get all family member locations (JSON)
### Navigation Integration
Family link added to main navbar:
- Shows "Family" if user is in a family
- Shows "Create Family" if user is not in a family
## User Interface
### Invitation Landing Page (Enhanced)
Beautiful invitation acceptance page featuring:
- Hero section with gradient background and family icon
- 4 benefit cards explaining family features:
- Share Location Data
- Track Your Location History
- Stay Connected
- Full Control & Privacy
- Invitation details (family name, inviter, expiration)
- Conditional CTAs based on authentication status:
- Not logged in: "Create Account & Join Family" button
- Logged in: "Accept Invitation & Join Family" button
- Dark mode support throughout
- Links to login/register with invitation token preservation
### Family Dashboard
- Family name and member count
- Interactive Leaflet map showing all family members
- Family member markers with auto-zoom on layer enable
- Real-time tooltips showing "Last updated: [timestamp]"
- Detailed popup on click with email and coordinates
- Member list with avatars and roles
- Pending invitations section (owners only)
- Invite member button (opens modal)
- Family settings and leave buttons
### Create Family Form
- Simple name input
- Feature benefits explanation
- Self-hosted vs cloud feature gating
### Family Settings
- Edit family name
- Delete family option (only if no other members)
- Danger zone warnings
### Map Visualization Features
Family member markers include:
- Colored circular markers with email initials
- Automatic tooltip showing last update time
- Click for detailed popup with email and coordinates
- Auto-zoom to fit all members when layer enabled
- Single member: centers at zoom 13
- Multiple members: fits bounds with padding
## Stimulus Controllers
**family_members_controller.js**
- Manages family member layer on map
- Creates markers with initials and colors
- Generates tooltips and popups
- Auto-zoom functionality when layer enabled
- Theme-aware styling for tooltips/popups
## Feature Gating
### DawarichSettings Integration
- `family_feature_enabled?` - Check if family feature is available
- Free for self-hosted instances
- Subscription-based for Dawarich Cloud (future)
- `family_max_members` - Configurable member limit per tier
## Testing Coverage
### Model Tests (68+ tests)
- Association validations
- Business rule enforcement
- User helper methods
- Invitation token generation and expiry
### Service Tests (53+ tests)
- Create, Invite, AcceptInvitation, Leave services
- Success and failure scenarios
- Error message validation
- Transaction rollback verification
### Controller Tests
- Authorization enforcement
- Successful operations
- Error handling
- Redirect logic
### Integration Tests
- Complete invitation flow
- Email delivery
- Notification creation
- Multi-user scenarios
### System Tests
- UI interactions
- Form submissions
- Modal interactions
- Map visualization
## Implementation Files
### Models
- `app/models/family.rb`
- `app/models/family_membership.rb`
- `app/models/family_invitation.rb`
- User model extensions
### Services
- `app/services/families/create.rb`
- `app/services/families/invite.rb`
- `app/services/families/accept_invitation.rb`
- `app/services/families/leave.rb`
- `app/services/families/location_sharing_service.rb`
### Controllers
- `app/controllers/families_controller.rb`
- `app/controllers/family_memberships_controller.rb`
- `app/controllers/family_invitations_controller.rb`
- `app/controllers/users/sessions_controller.rb`
### Views
- `app/views/families/` - Dashboard, create, edit
- `app/views/family_invitations/show.html.erb` - Enhanced landing page
- `app/views/devise/sessions/new.html.erb` - Login with invitation context
- `app/views/devise/registrations/new.html.erb` - Registration with invitation
### JavaScript
- `app/javascript/controllers/family_members_controller.js`
### Policies
- `app/policies/family_policy.rb`
- `app/policies/family_membership_policy.rb`
- `app/policies/family_invitation_policy.rb`
### Mailers
- `app/mailers/family_mailer.rb`
- Email templates for invitations
### Migrations
- `db/migrate/..._create_families.rb`
- `db/migrate/..._create_family_memberships.rb`
- `db/migrate/..._create_family_invitations.rb`
## Routes Summary
Nested resources under families:
```ruby
resources :families do
member do
delete :leave
patch :update_location_sharing
end
resources :invitations, except: %i[edit update], controller: 'family_invitations' do
member do
post :accept
end
end
resources :members, only: %i[index show destroy], controller: 'family_memberships'
end
# Public family invitation acceptance (no auth required)
get 'invitations/:id', to: 'family_invitations#show', as: :public_invitation
```
Custom Devise routes for invitation flow:
```ruby
devise_for :users, controllers: {
registrations: 'users/registrations',
sessions: 'users/sessions'
}
```
API routes for family locations:
```ruby
namespace :api do
namespace :v1 do
resources :families, only: [] do
collection do
get :locations
end
end
end
end
```
## Security Considerations
1. **Token-based Invitations** - Secure, unguessable tokens with 7-day expiry
2. **Sequential IDs** - Standard bigint primary keys for performance
3. **Authorization Policies** - Comprehensive Pundit policies for all actions
4. **Data Privacy** - Users control location sharing settings
5. **Account Protection** - Owners cannot delete accounts with active members
6. **Email Validation** - Proper format validation for invitations
## Performance Considerations
1. **Database Indexes** - Proper indexing on foreign keys and common queries
2. **Eager Loading** - Use `includes()` for associations
3. **Caching** - Family locations cached for map display
4. **Background Jobs** - Sidekiq for email sending
5. **Transaction Safety** - All operations wrapped in database transactions
## Implementation Phases Summary
### ✅ Phase 1: Database Foundation - COMPLETED
Database tables, models, associations, validations, and comprehensive tests
### ✅ Phase 2: Core Business Logic - COMPLETED
Service classes, email system, policies, error handling
### ✅ Phase 3: Controllers and Routes - COMPLETED
Controllers, authorization, custom Devise integration
### ✅ Phase 4: User Interface - COMPLETED
Views, enhanced invitation page, map integration, Stimulus controllers, dark mode
### ✅ Phase 5: Polish and Enhancements - COMPLETED
Map features (auto-zoom, tooltips, popups), error handling, feature gating
## Future Enhancements
1. **Historical Location Sharing** - Allow sharing location history with permissions
2. **Family Messaging** - Simple messaging between family members
3. **Geofencing** - Notifications when members enter/leave areas
4. **Family Events** - Plan and track family trips together
5. **Emergency Features** - Quick location sharing in emergencies
6. **Mobile Push Notifications** - Real-time location updates
7. **Family Statistics** - Aggregate travel statistics
8. **Multiple Families** - Allow users in multiple families with different roles
9. **Rate Limiting** - Invitation sending rate limits
---
**Feature Status**: ✅ Production Ready
All planned features have been implemented, tested, and are ready for deployment. The family feature maintains Dawarich's patterns for security, privacy, and performance while providing a comprehensive family location sharing experience.
## Recent UI/UX Improvements
### Invitation Flow Enhancement
- Beautiful landing page with gradient design
- Clear benefit explanations before signup
- Dark mode support throughout
- Proper token preservation through login/registration
- Conditional CTAs based on authentication state
### Map Visualization
- Auto-zoom to show all family members when layer enabled
- Real-time tooltips with "Last updated: [time]"
- Detailed popups showing email and coordinates on click
- Theme-aware styling for all map elements
- Smooth user experience with proper bounds handling
These improvements ensure users understand the value proposition before committing and have an excellent experience viewing family locations on the map.

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true
class FamilyInvitationsController < ApplicationController
class Family::InvitationsController < ApplicationController
before_action :authenticate_user!, except: %i[show accept]
before_action :ensure_family_feature_enabled!, except: %i[show accept]
before_action :set_family, except: %i[show accept]

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true
class FamilyMembershipsController < ApplicationController
class Family::MembershipsController < ApplicationController
before_action :authenticate_user!
before_action :ensure_family_feature_enabled!
before_action :set_family

View file

@ -64,16 +64,16 @@ Rails.application.routes.draw do
delete :leave
patch :update_location_sharing
end
resources :invitations, except: %i[edit update], controller: 'family_invitations' do
resources :invitations, except: %i[edit update], controller: 'family/invitations' do
member do
post :accept
end
end
resources :members, only: %i[destroy], controller: 'family_memberships'
resources :members, only: %i[destroy], controller: 'family/memberships'
end
# Public family invitation acceptance (no auth required)
get 'invitations/:id', to: 'family_invitations#show', as: :public_invitation
get 'invitations/:id', to: 'family/invitations#show', as: :public_invitation
# end
resources :points, only: %i[index] do
collection do

View file

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe 'Family Invitations', type: :request do
RSpec.describe 'Family::Invitations', type: :request do
let(:user) { create(:user) }
let(:family) { create(:family, creator: user) }
let!(:membership) { create(:family_membership, user: user, family: family, role: :owner) }

View file

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe 'Family Memberships', type: :request do
RSpec.describe 'Family::Memberships', type: :request do
let(:user) { create(:user) }
let(:family) { create(:family, creator: user) }
let!(:owner_membership) { create(:family_membership, user: user, family: family, role: :owner) }