Files
smoothschedule/frontend/src/api/auth.ts
poduck a4b23e44b6 feat(messaging): Add broadcast messaging system for owners and managers
- Add BroadcastMessage and MessageRecipient models for sending messages to groups or individuals
- Add Messages page with compose form and sent messages list
- Support targeting by role (owners, managers, staff, customers) or individual users
- Add can_send_messages permission (owners always, managers by default with revocable permission)
- Add autofill search dropdown with infinite scroll for selecting individual recipients
- Add staff permission toggle for managers' messaging access
- Integrate Messages link in sidebar for users with permission

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

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

147 lines
3.3 KiB
TypeScript

/**
* Authentication API
*/
import apiClient from './client';
export interface LoginCredentials {
email: string;
password: string;
}
import { UserRole } from '../types';
export interface QuotaOverage {
id: number;
quota_type: string;
display_name: string;
current_usage: number;
allowed_limit: number;
overage_amount: number;
days_remaining: number;
grace_period_ends_at: string;
}
export interface MasqueradeStackEntry {
user_id: number;
username: string;
role: UserRole;
business_id?: number;
business_subdomain?: string;
}
export interface LoginResponse {
// Regular login success
access?: string;
refresh?: string;
user?: {
id: number;
username: string;
email: string;
name: string;
role: UserRole;
avatar_url?: string;
email_verified?: boolean;
is_staff: boolean;
is_superuser: boolean;
business?: number;
business_name?: string;
business_subdomain?: string;
can_send_messages?: boolean;
};
masquerade_stack?: MasqueradeStackEntry[];
// MFA challenge response
mfa_required?: boolean;
user_id?: number;
mfa_methods?: ('SMS' | 'TOTP' | 'BACKUP')[];
phone_last_4?: string;
}
export interface User {
id: number;
username: string;
email: string;
name: string;
role: UserRole;
avatar_url?: string;
email_verified?: boolean;
is_staff: boolean;
is_superuser: boolean;
business?: number;
business_name?: string;
business_subdomain?: string;
permissions?: Record<string, boolean>;
can_invite_staff?: boolean;
can_access_tickets?: boolean;
can_edit_schedule?: boolean;
can_send_messages?: boolean;
linked_resource_id?: number;
quota_overages?: QuotaOverage[];
}
/**
* Login user
*/
export const login = async (credentials: LoginCredentials): Promise<LoginResponse> => {
const response = await apiClient.post<LoginResponse>('/auth/login/', credentials);
return response.data;
};
/**
* Logout user
*/
export const logout = async (): Promise<void> => {
await apiClient.post('/auth/logout/');
};
/**
* Get current user
*/
export const getCurrentUser = async (): Promise<User> => {
const response = await apiClient.get<User>('/auth/me/');
return response.data;
};
/**
* Refresh access token
*/
export const refreshToken = async (refresh: string): Promise<{ access: string }> => {
const response = await apiClient.post('/auth/refresh/', { refresh });
return response.data;
};
/**
* Masquerade as another user (hijack)
*/
export const masquerade = async (
user_pk: number,
hijack_history?: MasqueradeStackEntry[]
): Promise<LoginResponse> => {
const response = await apiClient.post<LoginResponse>(
'/auth/hijack/acquire/',
{ user_pk, hijack_history }
);
return response.data;
};
/**
* Stop masquerading and return to previous user
*/
export const stopMasquerade = async (
masquerade_stack: MasqueradeStackEntry[]
): Promise<LoginResponse> => {
const response = await apiClient.post<LoginResponse>(
'/auth/hijack/release/',
{ masquerade_stack }
);
return response.data;
};
/**
* Request password reset email
*/
export const forgotPassword = async (email: string): Promise<{ message: string }> => {
const response = await apiClient.post<{ message: string }>('/auth/password-reset/', { email });
return response.data;
};