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:
poduck
2025-11-27 01:43:20 -05:00
commit 2e111364a2
567 changed files with 96410 additions and 0 deletions

View File

@@ -0,0 +1,193 @@
/**
* Platform Settings Hooks
*/
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import apiClient from '../api/client';
export interface PlatformSettings {
stripe_secret_key_masked: string;
stripe_publishable_key_masked: string;
stripe_webhook_secret_masked: string;
stripe_account_id: string;
stripe_account_name: string;
stripe_keys_validated_at: string | null;
stripe_validation_error: string;
has_stripe_keys: boolean;
stripe_keys_from_env: boolean;
updated_at: string;
}
export interface StripeKeysUpdate {
stripe_secret_key?: string;
stripe_publishable_key?: string;
stripe_webhook_secret?: string;
}
export interface SubscriptionPlan {
id: number;
name: string;
description: string;
plan_type: 'base' | 'addon';
stripe_product_id: string;
stripe_price_id: string;
price_monthly: string | null;
price_yearly: string | null;
business_tier: string;
features: string[];
transaction_fee_percent: string;
transaction_fee_fixed: string;
is_active: boolean;
is_public: boolean;
created_at: string;
updated_at: string;
}
export interface SubscriptionPlanCreate {
name: string;
description?: string;
plan_type?: 'base' | 'addon';
price_monthly?: number | null;
price_yearly?: number | null;
business_tier?: string;
features?: string[];
transaction_fee_percent?: number;
transaction_fee_fixed?: number;
is_active?: boolean;
is_public?: boolean;
create_stripe_product?: boolean;
stripe_product_id?: string;
stripe_price_id?: string;
}
/**
* Hook to get platform settings
*/
export const usePlatformSettings = () => {
return useQuery<PlatformSettings>({
queryKey: ['platformSettings'],
queryFn: async () => {
const { data } = await apiClient.get('/api/platform/settings/');
return data;
},
staleTime: 5 * 60 * 1000, // 5 minutes
});
};
/**
* Hook to update platform Stripe keys
*/
export const useUpdateStripeKeys = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (keys: StripeKeysUpdate) => {
const { data } = await apiClient.post('/api/platform/settings/stripe/keys/', keys);
return data;
},
onSuccess: (data) => {
queryClient.setQueryData(['platformSettings'], data);
},
});
};
/**
* Hook to validate platform Stripe keys
*/
export const useValidateStripeKeys = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async () => {
const { data } = await apiClient.post('/api/platform/settings/stripe/validate/');
return data;
},
onSuccess: (data) => {
if (data.settings) {
queryClient.setQueryData(['platformSettings'], data.settings);
}
},
});
};
/**
* Hook to get subscription plans
*/
export const useSubscriptionPlans = () => {
return useQuery<SubscriptionPlan[]>({
queryKey: ['subscriptionPlans'],
queryFn: async () => {
const { data } = await apiClient.get('/api/platform/subscription-plans/');
return data;
},
staleTime: 5 * 60 * 1000,
});
};
/**
* Hook to create a subscription plan
*/
export const useCreateSubscriptionPlan = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (plan: SubscriptionPlanCreate) => {
const { data } = await apiClient.post('/api/platform/subscription-plans/', plan);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['subscriptionPlans'] });
},
});
};
/**
* Hook to update a subscription plan
*/
export const useUpdateSubscriptionPlan = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ id, ...updates }: Partial<SubscriptionPlan> & { id: number }) => {
const { data } = await apiClient.patch(`/api/platform/subscription-plans/${id}/`, updates);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['subscriptionPlans'] });
},
});
};
/**
* Hook to delete a subscription plan
*/
export const useDeleteSubscriptionPlan = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (id: number) => {
const { data } = await apiClient.delete(`/api/platform/subscription-plans/${id}/`);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['subscriptionPlans'] });
},
});
};
/**
* Hook to sync plans with Stripe
*/
export const useSyncPlansWithStripe = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async () => {
const { data } = await apiClient.post('/api/platform/subscription-plans/sync_with_stripe/');
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['subscriptionPlans'] });
},
});
};