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,154 @@
/**
* Payment Hooks
* React Query hooks for payment configuration management
*/
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import * as paymentsApi from '../api/payments';
// ============================================================================
// Query Keys
// ============================================================================
export const paymentKeys = {
all: ['payments'] as const,
config: () => [...paymentKeys.all, 'config'] as const,
apiKeys: () => [...paymentKeys.all, 'apiKeys'] as const,
connectStatus: () => [...paymentKeys.all, 'connectStatus'] as const,
};
// ============================================================================
// Unified Configuration Hook
// ============================================================================
/**
* Get unified payment configuration status.
* Returns the complete payment setup for the business.
*/
export const usePaymentConfig = () => {
return useQuery({
queryKey: paymentKeys.config(),
queryFn: () => paymentsApi.getPaymentConfig().then(res => res.data),
staleTime: 30 * 1000, // 30 seconds
});
};
// ============================================================================
// API Keys Hooks (Free Tier)
// ============================================================================
/**
* Get current API key configuration (masked).
*/
export const useApiKeys = () => {
return useQuery({
queryKey: paymentKeys.apiKeys(),
queryFn: () => paymentsApi.getApiKeys().then(res => res.data),
staleTime: 30 * 1000,
});
};
/**
* Validate API keys without saving.
*/
export const useValidateApiKeys = () => {
return useMutation({
mutationFn: ({ secretKey, publishableKey }: { secretKey: string; publishableKey: string }) =>
paymentsApi.validateApiKeys(secretKey, publishableKey).then(res => res.data),
});
};
/**
* Save API keys.
*/
export const useSaveApiKeys = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ secretKey, publishableKey }: { secretKey: string; publishableKey: string }) =>
paymentsApi.saveApiKeys(secretKey, publishableKey).then(res => res.data),
onSuccess: () => {
// Invalidate payment config to refresh status
queryClient.invalidateQueries({ queryKey: paymentKeys.config() });
queryClient.invalidateQueries({ queryKey: paymentKeys.apiKeys() });
},
});
};
/**
* Re-validate stored API keys.
*/
export const useRevalidateApiKeys = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => paymentsApi.revalidateApiKeys().then(res => res.data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: paymentKeys.config() });
queryClient.invalidateQueries({ queryKey: paymentKeys.apiKeys() });
},
});
};
/**
* Delete stored API keys.
*/
export const useDeleteApiKeys = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => paymentsApi.deleteApiKeys().then(res => res.data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: paymentKeys.config() });
queryClient.invalidateQueries({ queryKey: paymentKeys.apiKeys() });
},
});
};
// ============================================================================
// Stripe Connect Hooks (Paid Tiers)
// ============================================================================
/**
* Get current Connect account status.
*/
export const useConnectStatus = () => {
return useQuery({
queryKey: paymentKeys.connectStatus(),
queryFn: () => paymentsApi.getConnectStatus().then(res => res.data),
staleTime: 30 * 1000,
// Only fetch if we might have a Connect account
enabled: true,
});
};
/**
* Initiate Connect account onboarding.
*/
export const useConnectOnboarding = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ refreshUrl, returnUrl }: { refreshUrl: string; returnUrl: string }) =>
paymentsApi.initiateConnectOnboarding(refreshUrl, returnUrl).then(res => res.data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: paymentKeys.config() });
queryClient.invalidateQueries({ queryKey: paymentKeys.connectStatus() });
},
});
};
/**
* Refresh Connect onboarding link.
*/
export const useRefreshConnectLink = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ refreshUrl, returnUrl }: { refreshUrl: string; returnUrl: string }) =>
paymentsApi.refreshConnectOnboardingLink(refreshUrl, returnUrl).then(res => res.data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: paymentKeys.connectStatus() });
},
});
};