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,118 @@
/**
* Resource Management Hooks
*/
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import apiClient from '../api/client';
import { Resource, ResourceType } from '../types';
interface ResourceFilters {
type?: ResourceType;
}
/**
* Hook to fetch resources with optional type filter
*/
export const useResources = (filters?: ResourceFilters) => {
return useQuery<Resource[]>({
queryKey: ['resources', filters],
queryFn: async () => {
const params = new URLSearchParams();
if (filters?.type) params.append('type', filters.type);
const { data } = await apiClient.get(`/api/resources/?${params}`);
// Transform backend format to frontend format
return data.map((r: any) => ({
id: String(r.id),
name: r.name,
type: r.type as ResourceType,
userId: r.user_id ? String(r.user_id) : undefined,
}));
},
});
};
/**
* Hook to get a single resource
*/
export const useResource = (id: string) => {
return useQuery<Resource>({
queryKey: ['resources', id],
queryFn: async () => {
const { data } = await apiClient.get(`/api/resources/${id}/`);
return {
id: String(data.id),
name: data.name,
type: data.type as ResourceType,
userId: data.user_id ? String(data.user_id) : undefined,
};
},
enabled: !!id,
});
};
/**
* Hook to create a resource
*/
export const useCreateResource = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (resourceData: Omit<Resource, 'id'>) => {
const backendData = {
name: resourceData.name,
type: resourceData.type,
user: resourceData.userId ? parseInt(resourceData.userId) : null,
timezone: 'UTC', // Default timezone
};
const { data } = await apiClient.post('/api/resources/', backendData);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['resources'] });
},
});
};
/**
* Hook to update a resource
*/
export const useUpdateResource = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ id, updates }: { id: string; updates: Partial<Resource> }) => {
const backendData: any = {};
if (updates.name) backendData.name = updates.name;
if (updates.type) backendData.type = updates.type;
if (updates.userId !== undefined) {
backendData.user = updates.userId ? parseInt(updates.userId) : null;
}
const { data } = await apiClient.patch(`/api/resources/${id}/`, backendData);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['resources'] });
},
});
};
/**
* Hook to delete a resource
*/
export const useDeleteResource = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (id: string) => {
await apiClient.delete(`/api/resources/${id}/`);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['resources'] });
},
});
};