Files
smoothschedule/frontend/src/api/auth.ts
poduck 410b46a896 feat: Add time block approval workflow and staff permission system
- Add TimeBlock approval status with manager approval workflow
- Create core mixins for staff permission restrictions (DenyStaffWritePermission, etc.)
- Add StaffDashboard page for staff-specific views
- Refactor MyAvailability page for time block management
- Update field mobile status machine and views
- Add per-user permission overrides via JSONField
- Document core mixins and permission system in CLAUDE.md

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 17:49:37 -05:00

137 lines
2.9 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;
};
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;
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;
};