When VITE_API_URL=/api, axios baseURL is already set to /api. However, all endpoint calls included the /api/ prefix, creating double paths like /api/api/auth/login/. Removed /api/ prefix from 81 API endpoint calls across 22 files: - src/api/auth.ts - Fixed login, logout, me, refresh, hijack endpoints - src/api/client.ts - Fixed token refresh endpoint - src/api/profile.ts - Fixed all profile, email, password, MFA, sessions endpoints - src/hooks/*.ts - Fixed all remaining API calls (users, appointments, resources, etc) - src/pages/*.tsx - Fixed signup and email verification endpoints This ensures API requests use the correct path: /api/auth/login/ instead of /api/api/auth/login/ 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
120 lines
3.3 KiB
TypeScript
120 lines
3.3 KiB
TypeScript
/**
|
|
* Customer Management Hooks
|
|
*/
|
|
|
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import apiClient from '../api/client';
|
|
import { Customer } from '../types';
|
|
|
|
interface CustomerFilters {
|
|
status?: 'Active' | 'Inactive' | 'Blocked';
|
|
search?: string;
|
|
}
|
|
|
|
/**
|
|
* Hook to fetch customers with optional filters
|
|
*/
|
|
export const useCustomers = (filters?: CustomerFilters) => {
|
|
return useQuery<Customer[]>({
|
|
queryKey: ['customers', filters],
|
|
queryFn: async () => {
|
|
const params = new URLSearchParams();
|
|
if (filters?.status) params.append('status', filters.status);
|
|
if (filters?.search) params.append('search', filters.search);
|
|
|
|
const { data } = await apiClient.get(`/customers/?${params}`);
|
|
|
|
// Transform backend format to frontend format
|
|
return data.map((c: any) => ({
|
|
id: String(c.id),
|
|
name: c.name || c.user?.name || '',
|
|
email: c.email || c.user?.email || '',
|
|
phone: c.phone || '',
|
|
city: c.city,
|
|
state: c.state,
|
|
zip: c.zip,
|
|
totalSpend: parseFloat(c.total_spend || 0),
|
|
lastVisit: c.last_visit ? new Date(c.last_visit) : null,
|
|
status: c.status,
|
|
avatarUrl: c.avatar_url,
|
|
tags: c.tags || [],
|
|
userId: String(c.user_id || c.user),
|
|
paymentMethods: [], // Will be populated when payment feature is implemented
|
|
user_data: c.user_data, // Include user_data for masquerading
|
|
}));
|
|
},
|
|
retry: false, // Don't retry on 404 - endpoint may not exist yet
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to create a customer
|
|
*/
|
|
export const useCreateCustomer = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (customerData: Partial<Customer>) => {
|
|
const backendData = {
|
|
user: customerData.userId ? parseInt(customerData.userId) : undefined,
|
|
phone: customerData.phone,
|
|
city: customerData.city,
|
|
state: customerData.state,
|
|
zip: customerData.zip,
|
|
status: customerData.status,
|
|
avatar_url: customerData.avatarUrl,
|
|
tags: customerData.tags,
|
|
};
|
|
|
|
const { data } = await apiClient.post('/customers/', backendData);
|
|
return data;
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['customers'] });
|
|
},
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to update a customer
|
|
*/
|
|
export const useUpdateCustomer = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({ id, updates }: { id: string; updates: Partial<Customer> }) => {
|
|
const backendData = {
|
|
phone: updates.phone,
|
|
city: updates.city,
|
|
state: updates.state,
|
|
zip: updates.zip,
|
|
status: updates.status,
|
|
avatar_url: updates.avatarUrl,
|
|
tags: updates.tags,
|
|
};
|
|
|
|
const { data } = await apiClient.patch(`/customers/${id}/`, backendData);
|
|
return data;
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['customers'] });
|
|
},
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to delete a customer
|
|
*/
|
|
export const useDeleteCustomer = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (id: string) => {
|
|
await apiClient.delete(`/customers/${id}/`);
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['customers'] });
|
|
},
|
|
});
|
|
};
|