- UserTenantFilteredMixin now uses request.tenant (from django-tenants
middleware) instead of request.user.tenant for filtering
- ResourceSerializer._get_valid_user uses request.tenant for validation
- Frontend useResources sends user_id instead of user field
This fixes 400 errors when creating staff resources because the tenant
context is now correctly derived from the subdomain being accessed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Platform-level users (owners) may have tenant=None on their user record
but still access tenant subdomains. The _get_valid_user method now uses
request.tenant (from django-tenants middleware) which is set based on
the subdomain being accessed, not the user's tenant FK.
This fixes 400 Bad Request errors when platform users try to create
resources with staff assignments.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add frontend unit tests with Vitest for components, hooks, pages, and utilities
- Add backend tests for webhooks, notifications, middleware, and edge cases
- Add ForgotPassword, NotFound, and ResetPassword pages
- Add migration for orphaned staff resources conversion
- Add coverage directory to gitignore (generated reports)
- Various bug fixes and improvements from previous work
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add BroadcastMessage and MessageRecipient models for sending messages to groups or individuals
- Add Messages page with compose form and sent messages list
- Support targeting by role (owners, managers, staff, customers) or individual users
- Add can_send_messages permission (owners always, managers by default with revocable permission)
- Add autofill search dropdown with infinite scroll for selecting individual recipients
- Add staff permission toggle for managers' messaging access
- Integrate Messages link in sidebar for users with permission
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add pre_save signal to track changes to approved time blocks
- Reset to PENDING status when staff modifies approved time-off
- Send re-approval notifications to managers with changed fields
- Update email templates for modified requests
- Allow managers to have self-approval permission revoked (default: allowed)
A changed request is treated as a new request requiring re-approval.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create dateUtils.ts with helpers for UTC conversion and timezone display
- Add TimezoneSerializerMixin to include business_timezone in API responses
- Update GeneralSettings timezone dropdown with IANA identifiers
- Apply timezone mixin to Event, TimeBlock, and field mobile serializers
- Document timezone architecture in CLAUDE.md
All times stored in UTC, converted for display based on business timezone.
If business_timezone is null, uses user's local timezone.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add testing documentation emphasizing mocked unit tests over slow
database-hitting integration tests due to django-tenants overhead.
Guidelines include:
- Testing pyramid philosophy (prefer unit tests)
- Unit test examples with mocks
- Serializer and ViewSet testing patterns
- When to use integration tests (sparingly)
- Repository pattern for testable code
- Dependency injection examples
- Test file structure conventions
- Commands for running tests with coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Follow cookiecutter-django convention by placing tests in dedicated
tests/ directories within each app instead of single tests.py files.
Changes:
- Created tests/ directories with __init__.py for all 13 apps
- Moved analytics/tests.py → analytics/tests/test_views.py
- Moved schedule/test_export.py → schedule/tests/test_export.py
- Moved platform/api/tests_token_security.py → platform/api/tests/test_token_security.py
- Deleted empty placeholder tests.py files
All apps now have a tests/ directory ready for proper test organization.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Clicking a time-off request notification now navigates to the
time blocks page where pending requests can be reviewed.
- Added Clock icon for time-off request notifications
- Handle notification.data.type === 'time_off_request' to navigate to /time-blocks
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add TimeBlock approval status with manager approval workflow
- Create core mixins for staff permission restrictions (DenyStaffWritePermission, etc.)
- Add StaffDashboard page for staff-specific views
- Refactor MyAvailability page for time block management
- Update field mobile status machine and views
- Add per-user permission overrides via JSONField
- Document core mixins and permission system in CLAUDE.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Backend: Restrict staff from accessing resources, customers, services, and tasks APIs
- Frontend: Hide management sidebar links from staff members
- Add StaffSchedule page with vertical timeline view of appointments
- Add StaffHelp page with staff-specific documentation
- Return linked_resource_id and can_edit_schedule in user profile for staff
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add React Native Expo field app for mobile staff
- Use main /appointments/ endpoint with date range support
- Add X-Business-Subdomain header for tenant context
- Support day/week view navigation
- Remove WebSocket console logging from frontend
- Update AppointmentStatus type to include all backend statuses
- Add responsive status legend to scheduler header
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create multi-resolution favicon.ico (48x48, 32x32, 16x16) from logo
- Add apple-touch-icon.png for iOS devices
- Update index.html to use new favicon
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add contracts_enabled field to SubscriptionPlan model
- Add contracts toggle to plan create/edit modal in platform settings
- Hide contracts menu item for tenants without contracts permission
- Protect /contracts routes with canUse('contracts') check
- Add HasContractsPermission to contracts API ViewSets
- Add contracts to PlanPermissions interface and feature definitions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add export_legal endpoint for signed contracts that generates a ZIP with:
- Signed contract PDF
- Audit certificate PDF with signature details and hash verification
- Machine-readable signature_record.json
- Integrity verification report
- README documentation
- Add audit certificate template with:
- Contract and signature information
- Consent records with exact legal text
- Document integrity verification (SHA-256 hash comparison)
- ESIGN Act and UETA compliance statement
- Update ContractSigning page for ESIGN/UETA compliance:
- Consent checkbox text now matches backend-stored legal text
- Added proper legal notice with ESIGN Act references
- Add signed_at field to ContractListSerializer
- Add view/print buttons for signed contracts in Contracts page
- Allow viewing signed contracts via public signing URL
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The German helpComprehensive section had a different structure with 250
missing keys. Replaced with complete translations matching the English
structure used by HelpComprehensive.tsx.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The HelpComprehensive.tsx uses introduction.welcome but the translation
files only had introduction.title. Added the welcome key to match
the German translation structure.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced all hardcoded English text with i18n translation function calls
to enable proper internationalization. All sections now use
helpComprehensive.* translation keys that are already present in
en.json, es.json, fr.json, and de.json.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add Time Blocks section to HelpComprehensive.tsx with block levels,
types, recurrence patterns, and key features documentation
- Add complete helpComprehensive translations for en, es, fr, de
- Update HelpContracts.tsx styling
- Enhance FeaturesPage.tsx and HomePage.tsx
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add deposit_percent field back to Service model for percentage-based deposits
- Reorganize service form: variable pricing toggle at top, deposit toggle with
amount/percent options (percent only available for fixed pricing)
- Disable price field when variable pricing is enabled
- Add backend validation: variable pricing cannot use percentage deposits
- Update frontend types and hooks to handle deposit_percent field
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove deposit_percent field (doesn't work for variable pricing)
- Make deposit_amount default to 0 (no deposit)
- Deposit now applies to both variable and fixed pricing services
- Add requires_deposit and requires_saved_payment_method as computed properties
- Simplify frontend form with single deposit amount input
- Show deposit badge in service list when deposit > 0
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add ServiceInput interface for create/update operations
- Transform variable pricing fields in useServices query
- Handle deposit_amount and deposit_percent in mutations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Services can now have variable pricing where:
- Final price is determined after service completion
- A deposit (fixed amount or percentage) is collected at booking
- Customer's saved payment method is charged for remaining balance
Changes:
- Add variable_pricing, deposit_amount, deposit_percent fields to Service model
- Add service FK and final_price fields to Event model
- Add AWAITING_PAYMENT status to Event
- Add SetFinalPriceView endpoint to charge customer's saved card
- Add EventPricingInfoView endpoint for pricing details
- Update Services page with variable pricing toggle and deposit config
- Show "From $X" and deposit info in customer preview
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add CustomerBilling page for customers to view payment history and manage cards
- Create AddPaymentMethodModal with Stripe Elements for secure card saving
- Support both Stripe Connect and direct API payment modes
- Auto-set first payment method as default when no default exists
- Add dark mode support for Stripe card input styling
- Add customer billing API endpoints for payment history and saved cards
- Add stripe_customer_id field to User model for Stripe customer tracking
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add request tenant validation to all ViewSets (EventViewSet, ResourceViewSet,
ParticipantViewSet, CustomerViewSet, StaffViewSet) to prevent cross-tenant
data access via subdomain/header manipulation
- Change permission_classes from AllowAny to IsAuthenticated for EventViewSet
and ResourceViewSet
- Filter events for customers to only show appointments where they are a
participant
- Add customer field to EventSerializer to create Customer participants when
appointments are created
- Update CustomerDashboard to fetch appointments from API instead of mock data
- Fix TenantViewSet.destroy() to properly handle cross-schema cascade when
deleting tenants
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Backend login now accepts 'email' field (with backward compatibility)
- User creation (signup, invitation, customer) uses email as username
- Frontend login form updated with email input and validation
- Updated test users to use email addresses as usernames
- Updated all translation files (en, es, fr, de)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The CustomerSerializer was missing a create method to generate a unique
username, causing IntegrityError when trying to create customers.
- Add first_name and last_name as write-only fields
- Remove email from read_only_fields so it can be set on creation
- Generate username from email prefix (with counter for uniqueness)
- Fall back to UUID-based username if no email provided
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Restore the LanguageSelector component to the platform layout header,
allowing platform users to switch languages.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Dynamically set robots meta tag to noindex/nofollow when on any
subdomain (platform.*, demo.*, etc.). Only the root domain
marketing pages should be indexed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Set robots meta tag to noindex, nofollow (site not live)
- Update robots.txt with instructions for going live
- Add sitemap.xml with all marketing pages
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace "multi-tenant" wording with user-friendly alternatives
- Hero subheadline: "Secure" instead of "Multi-tenant"
- Feature title: "Enterprise Security" instead of "Multi-Tenant Architecture"
- Updated testimonials and FAQ to remove technical references
- Add comprehensive SEO meta tags to index.html:
- Meta description for search engines
- Open Graph tags for social sharing
- Twitter card meta tags
- Canonical URL and robots directives
- Update all language files (en, es, fr, de) with consistent changes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Simplified UI with Email, Business Name, and Subscription Tier fields
- Added collapsible "Override Tier Limits" section with sliding animation
- Permission options match platform settings structure (Payments, Communication, Customization, Plugins, Advanced, Enterprise)
- Permissions are loaded from subscription plans or fallback to static defaults
- Custom limits/permissions only sent to backend when override is checked
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- New TenantLandingPage component with 'Coming Soon' message
- Shows business name derived from subdomain
- Has 'Sign In' button that goes to /login
- 'Powered by SmoothSchedule' footer
- Will be customizable later for each tenant
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The button after tenant creation was misleading - users need to log in first.
Changed button text and URL to explicitly point to /login.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Wildcard subdomain routing is now working. Removed access logging
that was added for debugging.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
In YAML single-quoted strings, backslashes are literal characters.
'\\.' was being interpreted as two backslashes + dot, not as an
escaped dot in the regex.
Changed from '\\.smoothschedule\\.com' to '\.smoothschedule\.com'
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>