Initial commit: SmoothSchedule multi-tenant scheduling platform
This commit includes: - Django backend with multi-tenancy (django-tenants) - React + TypeScript frontend with Vite - Platform administration API with role-based access control - Authentication system with token-based auth - Quick login dev tools for testing different user roles - CORS and CSRF configuration for local development - Docker development environment setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
111
smoothschedule/platform_admin/serializers.py
Normal file
111
smoothschedule/platform_admin/serializers.py
Normal file
@@ -0,0 +1,111 @@
|
||||
"""
|
||||
Platform Serializers
|
||||
Serializers for platform-level operations (viewing tenants, users, metrics)
|
||||
"""
|
||||
from rest_framework import serializers
|
||||
from core.models import Tenant, Domain
|
||||
from smoothschedule.users.models import User
|
||||
|
||||
|
||||
class TenantSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for Tenant (Business) listing"""
|
||||
subdomain = serializers.SerializerMethodField()
|
||||
tier = serializers.CharField(source='subscription_tier')
|
||||
user_count = serializers.SerializerMethodField()
|
||||
owner = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Tenant
|
||||
fields = [
|
||||
'id', 'name', 'subdomain', 'tier', 'is_active',
|
||||
'created_on', 'user_count', 'owner', 'max_users',
|
||||
'max_resources', 'contact_email', 'phone'
|
||||
]
|
||||
read_only_fields = fields
|
||||
|
||||
def get_subdomain(self, obj):
|
||||
"""Get primary subdomain for this tenant"""
|
||||
primary_domain = obj.domains.filter(is_primary=True, is_custom_domain=False).first()
|
||||
if primary_domain:
|
||||
# Extract subdomain from domain (e.g., 'business1.lvh.me' -> 'business1')
|
||||
return primary_domain.domain.split('.')[0]
|
||||
return obj.schema_name
|
||||
|
||||
def get_user_count(self, obj):
|
||||
"""Get count of users in this tenant's schema"""
|
||||
# This requires querying the tenant schema
|
||||
# For now, return 0 - we can optimize this with annotations
|
||||
return 0
|
||||
|
||||
def get_owner(self, obj):
|
||||
"""Get the tenant owner user"""
|
||||
# Query public schema for users with this tenant as their business
|
||||
try:
|
||||
owner = User.objects.filter(
|
||||
role=User.Role.TENANT_OWNER,
|
||||
# Note: We need to add a tenant reference to User model
|
||||
).first()
|
||||
|
||||
if owner:
|
||||
return {
|
||||
'id': owner.id,
|
||||
'username': owner.username,
|
||||
'full_name': owner.full_name,
|
||||
'email': owner.email,
|
||||
'role': owner.role,
|
||||
}
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
class PlatformUserSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for User listing (platform view)"""
|
||||
business = serializers.SerializerMethodField()
|
||||
business_name = serializers.SerializerMethodField()
|
||||
business_subdomain = serializers.SerializerMethodField()
|
||||
full_name = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = [
|
||||
'id', 'email', 'username', 'first_name', 'last_name', 'full_name', 'role',
|
||||
'is_active', 'is_staff', 'is_superuser',
|
||||
'business', 'business_name', 'business_subdomain',
|
||||
'date_joined', 'last_login'
|
||||
]
|
||||
read_only_fields = fields
|
||||
|
||||
def get_full_name(self, obj):
|
||||
"""Get user's full name"""
|
||||
return obj.full_name
|
||||
|
||||
def get_business(self, obj):
|
||||
"""Get tenant ID if user belongs to a tenant"""
|
||||
if obj.tenant:
|
||||
return obj.tenant.id
|
||||
return None
|
||||
|
||||
def get_business_name(self, obj):
|
||||
"""Get tenant name if user belongs to a tenant"""
|
||||
if obj.tenant:
|
||||
return obj.tenant.name
|
||||
return None
|
||||
|
||||
def get_business_subdomain(self, obj):
|
||||
"""Get tenant subdomain if user belongs to a tenant"""
|
||||
if obj.tenant:
|
||||
primary_domain = obj.tenant.domains.filter(is_primary=True, is_custom_domain=False).first()
|
||||
if primary_domain:
|
||||
return primary_domain.domain.split('.')[0]
|
||||
return obj.tenant.schema_name
|
||||
return None
|
||||
|
||||
|
||||
class PlatformMetricsSerializer(serializers.Serializer):
|
||||
"""Serializer for platform dashboard metrics"""
|
||||
total_tenants = serializers.IntegerField()
|
||||
active_tenants = serializers.IntegerField()
|
||||
total_users = serializers.IntegerField()
|
||||
mrr = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
growth_rate = serializers.FloatField()
|
||||
Reference in New Issue
Block a user