Files
smoothschedule/frontend/src/hooks/useInvitations.ts
poduck 4cd6610f2a Fix double /api/ prefix in API endpoint calls
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>
2025-11-30 15:27:57 -05:00

166 lines
3.9 KiB
TypeScript

/**
* Staff Invitations Management Hooks
*/
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import apiClient from '../api/client';
export interface StaffInvitation {
id: number;
email: string;
role: 'TENANT_MANAGER' | 'TENANT_STAFF';
role_display: string;
status: 'PENDING' | 'ACCEPTED' | 'DECLINED' | 'EXPIRED' | 'CANCELLED';
invited_by: number | null;
invited_by_name: string | null;
created_at: string;
expires_at: string;
accepted_at: string | null;
create_bookable_resource: boolean;
resource_name: string;
permissions: Record<string, boolean>;
}
export interface InvitationDetails {
email: string;
role: string;
role_display: string;
business_name: string;
invited_by: string | null;
expires_at: string;
create_bookable_resource: boolean;
resource_name: string;
}
export interface StaffPermissions {
// Manager permissions
can_invite_staff?: boolean;
can_manage_resources?: boolean;
can_manage_services?: boolean;
can_view_reports?: boolean;
can_access_settings?: boolean;
can_refund_payments?: boolean;
// Staff permissions
can_view_all_schedules?: boolean;
can_manage_own_appointments?: boolean;
}
export interface CreateInvitationData {
email: string;
role: 'TENANT_MANAGER' | 'TENANT_STAFF';
create_bookable_resource?: boolean;
resource_name?: string;
permissions?: StaffPermissions;
}
/**
* Hook to fetch pending invitations for the current business
*/
export const useInvitations = () => {
return useQuery<StaffInvitation[]>({
queryKey: ['invitations'],
queryFn: async () => {
const { data } = await apiClient.get('/staff/invitations/');
return data;
},
});
};
/**
* Hook to create a new staff invitation
*/
export const useCreateInvitation = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (invitationData: CreateInvitationData) => {
const { data } = await apiClient.post('/staff/invitations/', invitationData);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['invitations'] });
},
});
};
/**
* Hook to cancel a pending invitation
*/
export const useCancelInvitation = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (invitationId: number) => {
await apiClient.delete(`/staff/invitations/${invitationId}/`);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['invitations'] });
},
});
};
/**
* Hook to resend an invitation email
*/
export const useResendInvitation = () => {
return useMutation({
mutationFn: async (invitationId: number) => {
const { data } = await apiClient.post(`/staff/invitations/${invitationId}/resend/`);
return data;
},
});
};
/**
* Hook to get invitation details by token (for acceptance page)
*/
export const useInvitationDetails = (token: string | null) => {
return useQuery<InvitationDetails>({
queryKey: ['invitation', token],
queryFn: async () => {
const { data } = await apiClient.get(`/staff/invitations/token/${token}/`);
return data;
},
enabled: !!token,
retry: false,
});
};
/**
* Hook to accept an invitation
*/
export const useAcceptInvitation = () => {
return useMutation({
mutationFn: async ({
token,
firstName,
lastName,
password,
}: {
token: string;
firstName: string;
lastName: string;
password: string;
}) => {
const { data } = await apiClient.post(`/staff/invitations/token/${token}/accept/`, {
first_name: firstName,
last_name: lastName,
password,
});
return data;
},
});
};
/**
* Hook to decline an invitation
*/
export const useDeclineInvitation = () => {
return useMutation({
mutationFn: async (token: string) => {
const { data } = await apiClient.post(`/staff/invitations/token/${token}/decline/`);
return data;
},
});
};