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,190 @@
/**
* Domain Management Hooks
* React Query hooks for NameSilo domain integration
*/
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import * as domainsApi from '../api/domains';
import type {
DomainAvailability,
DomainPrice,
DomainRegisterRequest,
DomainRegistration,
DomainSearchHistory,
} from '../api/domains';
// Query keys
const domainKeys = {
all: ['domains'] as const,
prices: () => [...domainKeys.all, 'prices'] as const,
registrations: () => [...domainKeys.all, 'registrations'] as const,
registration: (id: number) => [...domainKeys.registrations(), id] as const,
history: () => [...domainKeys.all, 'history'] as const,
search: (query: string, tlds: string[]) => [...domainKeys.all, 'search', query, tlds] as const,
};
// ============================================
// Search & Pricing
// ============================================
/**
* Hook to search for domain availability
*/
export const useDomainSearch = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ query, tlds }: { query: string; tlds?: string[] }) =>
domainsApi.searchDomains(query, tlds),
onSuccess: () => {
// Invalidate search history since new search was added
queryClient.invalidateQueries({ queryKey: domainKeys.history() });
},
});
};
/**
* Hook to get TLD pricing
*/
export const useDomainPrices = () => {
return useQuery({
queryKey: domainKeys.prices(),
queryFn: domainsApi.getDomainPrices,
staleTime: 5 * 60 * 1000, // 5 minutes
});
};
// ============================================
// Registration
// ============================================
/**
* Hook to register a new domain
*/
export const useRegisterDomain = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: DomainRegisterRequest) => domainsApi.registerDomain(data),
onSuccess: () => {
// Invalidate registrations list
queryClient.invalidateQueries({ queryKey: domainKeys.registrations() });
// Also invalidate custom domains since we auto-configure
queryClient.invalidateQueries({ queryKey: ['customDomains'] });
},
});
};
/**
* Hook to get all registered domains
*/
export const useRegisteredDomains = () => {
return useQuery({
queryKey: domainKeys.registrations(),
queryFn: domainsApi.getRegisteredDomains,
staleTime: 30 * 1000, // 30 seconds
});
};
/**
* Hook to get a single domain registration
*/
export const useDomainRegistration = (id: number) => {
return useQuery({
queryKey: domainKeys.registration(id),
queryFn: () => domainsApi.getDomainRegistration(id),
enabled: !!id,
});
};
// ============================================
// Domain Management
// ============================================
/**
* Hook to update nameservers
*/
export const useUpdateNameservers = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, nameservers }: { id: number; nameservers: string[] }) =>
domainsApi.updateNameservers(id, nameservers),
onSuccess: (data) => {
queryClient.setQueryData(domainKeys.registration(data.id), data);
queryClient.invalidateQueries({ queryKey: domainKeys.registrations() });
},
});
};
/**
* Hook to toggle auto-renewal
*/
export const useToggleAutoRenew = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, autoRenew }: { id: number; autoRenew: boolean }) =>
domainsApi.toggleAutoRenew(id, autoRenew),
onSuccess: (data) => {
queryClient.setQueryData(domainKeys.registration(data.id), data);
queryClient.invalidateQueries({ queryKey: domainKeys.registrations() });
},
});
};
/**
* Hook to renew a domain
*/
export const useRenewDomain = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, years }: { id: number; years?: number }) =>
domainsApi.renewDomain(id, years),
onSuccess: (data) => {
queryClient.setQueryData(domainKeys.registration(data.id), data);
queryClient.invalidateQueries({ queryKey: domainKeys.registrations() });
},
});
};
/**
* Hook to sync domain info from NameSilo
*/
export const useSyncDomain = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: number) => domainsApi.syncDomain(id),
onSuccess: (data) => {
queryClient.setQueryData(domainKeys.registration(data.id), data);
queryClient.invalidateQueries({ queryKey: domainKeys.registrations() });
},
});
};
// ============================================
// History
// ============================================
/**
* Hook to get search history
*/
export const useSearchHistory = () => {
return useQuery({
queryKey: domainKeys.history(),
queryFn: domainsApi.getSearchHistory,
staleTime: 60 * 1000, // 1 minute
});
};
// Re-export types for convenience
export type {
DomainAvailability,
DomainPrice,
DomainRegisterRequest,
DomainRegistration,
DomainSearchHistory,
RegistrantContact,
} from '../api/domains';