Demo Tenant: - Add block_emails field to Tenant model for demo accounts - Add is_email_blocked() and wrapper functions in email_service - Create reseed_demo management command with salon/spa theme - Add Celery beat task for daily reseed at midnight UTC - Create 100 appointments, 20 customers, 13 services, 12 resources Staff Roles: - Add StaffRole model with permission toggles - Create default roles: Full Access, Front Desk, Limited Staff - Add StaffRolesSettings page and hooks - Integrate role assignment in Staff management Bug Fixes: - Fix masquerade redirect using wrong role names (tenant_owner vs owner) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
96 lines
2.5 KiB
TypeScript
96 lines
2.5 KiB
TypeScript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import apiClient from '../api/client';
|
|
import { StaffRole, AvailablePermissions } from '../types';
|
|
|
|
/**
|
|
* Hook to fetch all staff roles for the current tenant
|
|
*/
|
|
export const useStaffRoles = () => {
|
|
return useQuery<StaffRole[]>({
|
|
queryKey: ['staffRoles'],
|
|
queryFn: async () => {
|
|
const { data } = await apiClient.get('/staff-roles/');
|
|
return data;
|
|
},
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to fetch a single staff role by ID
|
|
*/
|
|
export const useStaffRole = (id: number | null) => {
|
|
return useQuery<StaffRole>({
|
|
queryKey: ['staffRoles', id],
|
|
queryFn: async () => {
|
|
const { data } = await apiClient.get(`/staff-roles/${id}/`);
|
|
return data;
|
|
},
|
|
enabled: id !== null,
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to fetch available permission keys and their metadata
|
|
*/
|
|
export const useAvailablePermissions = () => {
|
|
return useQuery<AvailablePermissions>({
|
|
queryKey: ['staffRoles', 'availablePermissions'],
|
|
queryFn: async () => {
|
|
const { data } = await apiClient.get('/staff-roles/available_permissions/');
|
|
return data;
|
|
},
|
|
staleTime: 1000 * 60 * 60, // Cache for 1 hour - permissions don't change often
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to create a new staff role
|
|
*/
|
|
export const useCreateStaffRole = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (data: Partial<StaffRole>) => {
|
|
const response = await apiClient.post('/staff-roles/', data);
|
|
return response.data as StaffRole;
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['staffRoles'] });
|
|
},
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to update an existing staff role
|
|
*/
|
|
export const useUpdateStaffRole = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({ id, ...data }: { id: number } & Partial<StaffRole>) => {
|
|
const response = await apiClient.patch(`/staff-roles/${id}/`, data);
|
|
return response.data as StaffRole;
|
|
},
|
|
onSuccess: (_, variables) => {
|
|
queryClient.invalidateQueries({ queryKey: ['staffRoles'] });
|
|
queryClient.invalidateQueries({ queryKey: ['staffRoles', variables.id] });
|
|
},
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to delete a staff role
|
|
*/
|
|
export const useDeleteStaffRole = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (id: number) => {
|
|
await apiClient.delete(`/staff-roles/${id}/`);
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['staffRoles'] });
|
|
},
|
|
});
|
|
};
|