mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 17:21:38 -05:00
Move family controllers to their own namespace
This commit is contained in:
parent
f898f3aab0
commit
cfe319df9b
9 changed files with 8 additions and 452 deletions
444
FAMILY_PLAN.md
444
FAMILY_PLAN.md
|
|
@ -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.
|
||||
|
|
@ -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]
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
@ -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) }
|
||||
Loading…
Reference in a new issue