Files
smoothschedule/frontend/src/hooks/useBusiness.ts
poduck 8dc2248f1f feat: Add comprehensive test suite and misc improvements
- Add frontend unit tests with Vitest for components, hooks, pages, and utilities
- Add backend tests for webhooks, notifications, middleware, and edge cases
- Add ForgotPassword, NotFound, and ResetPassword pages
- Add migration for orphaned staff resources conversion
- Add coverage directory to gitignore (generated reports)
- Various bug fixes and improvements from previous work

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 02:36:46 -05:00

175 lines
6.3 KiB
TypeScript

/**
* Business Management Hooks
*/
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import apiClient from '../api/client';
import { Business } from '../types';
import { getCookie } from '../utils/cookies';
/**
* Hook to get current business
*/
export const useCurrentBusiness = () => {
// Check token outside the query to use as dependency
const token = getCookie('access_token');
return useQuery<Business | null>({
queryKey: ['currentBusiness', !!token], // Include token presence in query key to refetch when token changes
queryFn: async () => {
// Check if token exists before making request (from cookie)
const currentToken = getCookie('access_token');
if (!currentToken) {
return null; // No token, return null instead of making request
}
const { data } = await apiClient.get('/business/current/');
// Transform backend format to frontend format
return {
id: String(data.id),
name: data.name,
subdomain: data.subdomain,
primaryColor: data.primary_color || '#3B82F6', // Blue-500 default
secondaryColor: data.secondary_color || '#1E40AF', // Blue-800 default
logoUrl: data.logo_url,
emailLogoUrl: data.email_logo_url,
logoDisplayMode: data.logo_display_mode || 'text-only',
timezone: data.timezone || 'America/New_York',
timezoneDisplayMode: data.timezone_display_mode || 'business',
whitelabelEnabled: data.whitelabel_enabled,
plan: data.tier, // Map tier to plan
status: data.status,
joinedAt: data.created_at ? new Date(data.created_at) : undefined,
resourcesCanReschedule: data.resources_can_reschedule,
requirePaymentMethodToBook: data.require_payment_method_to_book,
cancellationWindowHours: data.cancellation_window_hours,
lateCancellationFeePercent: data.late_cancellation_fee_percent,
initialSetupComplete: data.initial_setup_complete,
websitePages: data.website_pages || {},
customerDashboardContent: data.customer_dashboard_content || [],
paymentsEnabled: data.payments_enabled ?? false,
// Platform-controlled permissions
canManageOAuthCredentials: data.can_manage_oauth_credentials || false,
// Plan permissions (what features are available based on subscription)
planPermissions: data.plan_permissions || {
sms_reminders: false,
webhooks: false,
api_access: false,
custom_domain: false,
white_label: false,
custom_oauth: false,
plugins: false,
can_create_plugins: false,
tasks: false,
export_data: false,
video_conferencing: false,
two_factor_auth: false,
masked_calling: false,
pos_system: false,
mobile_app: false,
contracts: false,
},
};
},
});
};
/**
* Hook to update business settings
*/
export const useUpdateBusiness = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (updates: Partial<Business>) => {
const backendData: any = {};
// Map frontend fields to backend fields
if (updates.name) backendData.name = updates.name;
if (updates.primaryColor !== undefined) backendData.primary_color = updates.primaryColor;
if (updates.secondaryColor !== undefined) backendData.secondary_color = updates.secondaryColor;
if (updates.logoUrl !== undefined) backendData.logo_url = updates.logoUrl;
if (updates.emailLogoUrl !== undefined) backendData.email_logo_url = updates.emailLogoUrl;
if (updates.logoDisplayMode !== undefined) backendData.logo_display_mode = updates.logoDisplayMode;
if (updates.timezone !== undefined) backendData.timezone = updates.timezone;
if (updates.timezoneDisplayMode !== undefined) backendData.timezone_display_mode = updates.timezoneDisplayMode;
if (updates.whitelabelEnabled !== undefined) {
backendData.whitelabel_enabled = updates.whitelabelEnabled;
}
if (updates.resourcesCanReschedule !== undefined) {
backendData.resources_can_reschedule = updates.resourcesCanReschedule;
}
if (updates.requirePaymentMethodToBook !== undefined) {
backendData.require_payment_method_to_book = updates.requirePaymentMethodToBook;
}
if (updates.cancellationWindowHours !== undefined) {
backendData.cancellation_window_hours = updates.cancellationWindowHours;
}
if (updates.lateCancellationFeePercent !== undefined) {
backendData.late_cancellation_fee_percent = updates.lateCancellationFeePercent;
}
if (updates.initialSetupComplete !== undefined) {
backendData.initial_setup_complete = updates.initialSetupComplete;
}
if (updates.websitePages !== undefined) {
backendData.website_pages = updates.websitePages;
}
if (updates.customerDashboardContent !== undefined) {
backendData.customer_dashboard_content = updates.customerDashboardContent;
}
const { data } = await apiClient.patch('/business/current/update/', backendData);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['currentBusiness'] });
},
});
};
/**
* Hook to get all resources for the current business
*/
export const useResources = () => {
return useQuery({
queryKey: ['resources'],
queryFn: async () => {
const { data } = await apiClient.get('/resources/');
return data;
},
staleTime: 5 * 60 * 1000, // 5 minutes
});
};
/**
* Hook to create a new resource
*/
export const useCreateResource = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (resourceData: { name: string; type: string; user_id?: string }) => {
const { data } = await apiClient.post('/resources/', resourceData);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['resources'] });
},
});
};
/**
* Hook to get all users for the current business
*/
export const useBusinessUsers = () => {
return useQuery({
queryKey: ['businessUsers'],
queryFn: async () => {
const { data } = await apiClient.get('/staff/');
return data;
},
staleTime: 5 * 60 * 1000, // 5 minutes
});
};