- 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>
The TCP router was intercepting wildcard subdomain traffic at the TCP layer
and sending it directly to nginx:80, bypassing HTTP routing entirely.
This caused 404 errors because nginx wasn't receiving proper HTTP routing.
Now relying on:
- TLS store's defaultGeneratedCert for wildcard certificate
- HTTP HostRegexp router for subdomain routing to nginx
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add support for specifying services to rebuild (e.g., ./deploy.sh traefik)
- Add --no-migrate flag to skip migrations for config-only changes
- Reduce wait time from 10s to 5s
Usage examples:
./deploy.sh # Build all, run migrations
./deploy.sh traefik --no-migrate # Only rebuild traefik, skip migrations
./deploy.sh django nginx # Build django and nginx, run migrations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add a TCP-level router using HostSNIRegexp to match unknown subdomains
at the TLS layer and terminate TLS with wildcard certificate.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add default TLS store with wildcard certificate for unknown SNIs
- Add priority=1 to subdomain-router for catch-all behavior
- Use proper Traefik v3 HostRegexp syntax with anchors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Separate acme.json storage for HTTP and DNS certificate resolvers
to prevent conflicts when requesting wildcard certificates.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Traefik v3 changed HostRegexp syntax from named capture groups to
standard regex. Added low priority to avoid matching specific routes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
HostRegexp patterns don't work with HTTP challenge because Traefik
can't request certificates for dynamic subdomains. Switched to DNS
challenge using DigitalOcean provider for *.smoothschedule.com wildcard.
Requires DO_AUTH_TOKEN environment variable to be set.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The subdomain-router was incorrectly sending tenant subdomain requests
directly to Django (API server), causing 404 errors. Now routes to nginx
which serves the React SPA and proxies /api/ requests to Django.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add elapsed time counter (MM:SS)
- Spread animation steps over ~30 seconds before final step
- Final step stays spinning (doesn't complete early)
- Progress bar caps at 90% until actually done, pulses on final step
- Show "Finalizing..." and helpful message during long final step
- Clear "45-90 seconds" time estimate upfront
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add multi-step animated loading indicator during tenant creation
- Fix blank completion screen (was checking wrong step number)
- Auto-verify email for users accepting tenant invitations
- Show progress bar and step-by-step status during database setup
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The post_save signal was trying to seed plugins before the tenant's
schema migrations had completed, causing a 500 error when accepting
tenant invitations. Using transaction.on_commit() ensures the schema
and tables exist before seeding.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Platform/tenant invitations should redirect to /tenant-onboard which has
the full onboarding wizard, not /accept-invite which is for staff invitations.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update useInvitationDetails to try platform tenant invitation first,
then fall back to staff invitation
- Update useAcceptInvitation to handle both invitation types
- Update useDeclineInvitation to handle both invitation types
- Pass invitation type from AcceptInvitePage to mutations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Requires changes to be committed and pushed before deploying
- Clones repo on first deploy, then uses git fetch/reset
- Preserves .envs secrets which are not in git
- Better for multi-developer workflows
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Don't redirect unauthenticated users to login when accessing public paths
like /accept-invite, /verify-email, or /tenant-onboard on subdomains.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add SMTP email backend support to production settings (EMAIL_HOST, EMAIL_PORT, etc.)
- Falls back to console backend if SMTP not configured
- Fix AcceptInvitePage to support both path parameter (/accept-invite/:token) and
query parameter (?token=...) formats for invitation tokens
- Add route for /accept-invite/:token in App.tsx
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>