Files
smoothschedule/frontend/src/api/quota.ts
poduck 08b51d1a5f feat: Quota overage system, updated tier pricing, and communication addons
Quota Overage System:
- Add QuotaOverage model for tracking resource/user quota overages
- Implement 30-day grace period with email notifications (immediate, 7-day, 1-day)
- Add QuotaWarningBanner component in BusinessLayout
- Add QuotaSettings page for managing overages and archiving resources
- Add Celery tasks for automated quota checks and expiration handling
- Add quota management API endpoints

Updated Tier Pricing (Stripe: 2.9% + $0.30):
- Free: No payments (requires addon)
- Starter: 4% + $0.40
- Professional: 3.5% + $0.35
- Business: 3.25% + $0.32
- Enterprise: 3% + $0.30

New Subscription Addons:
- Online Payments ($5/mo + 5% + $0.50) - for Free tier
- SMS Notifications ($10/mo) - enables SMS reminders
- Masked Calling ($15/mo) - enables anonymous calling

BusinessEditModal Improvements:
- Increased width to match PlanModal (max-w-3xl)
- Added all tier options with auto-update on tier change
- Added limits configuration and permissions sections

Backend Fixes:
- Fixed SubscriptionPlan serializer to include all communication fields
- Allow blank business_tier for addon plans
- Added migration for business_tier field changes

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 13:05:02 -05:00

104 lines
2.4 KiB
TypeScript

/**
* Quota Management API
*/
import apiClient from './client';
import { QuotaOverage } from './auth';
export interface QuotaUsage {
current: number;
limit: number;
display_name: string;
}
export interface QuotaStatus {
active_overages: QuotaOverage[];
usage: Record<string, QuotaUsage>;
}
export interface QuotaResource {
id: number;
name: string;
email?: string;
role?: string;
type?: string;
duration?: number;
price?: string;
created_at: string | null;
is_archived: boolean;
archived_at: string | null;
}
export interface QuotaResourcesResponse {
quota_type: string;
resources: QuotaResource[];
}
export interface ArchiveResponse {
archived_count: number;
current_usage: number;
limit: number;
is_resolved: boolean;
}
export interface QuotaOverageDetail extends QuotaOverage {
status: string;
created_at: string;
initial_email_sent_at: string | null;
week_reminder_sent_at: string | null;
day_reminder_sent_at: string | null;
archived_resource_ids: number[];
}
/**
* Get current quota status
*/
export const getQuotaStatus = async (): Promise<QuotaStatus> => {
const response = await apiClient.get<QuotaStatus>('/quota/status/');
return response.data;
};
/**
* Get resources for a specific quota type
*/
export const getQuotaResources = async (quotaType: string): Promise<QuotaResourcesResponse> => {
const response = await apiClient.get<QuotaResourcesResponse>(`/quota/resources/${quotaType}/`);
return response.data;
};
/**
* Archive resources to resolve quota overage
*/
export const archiveResources = async (
quotaType: string,
resourceIds: number[]
): Promise<ArchiveResponse> => {
const response = await apiClient.post<ArchiveResponse>('/quota/archive/', {
quota_type: quotaType,
resource_ids: resourceIds,
});
return response.data;
};
/**
* Unarchive a resource
*/
export const unarchiveResource = async (
quotaType: string,
resourceId: number
): Promise<{ success: boolean; resource_id: number }> => {
const response = await apiClient.post('/quota/unarchive/', {
quota_type: quotaType,
resource_id: resourceId,
});
return response.data;
};
/**
* Get details for a specific overage
*/
export const getOverageDetail = async (overageId: number): Promise<QuotaOverageDetail> => {
const response = await apiClient.get<QuotaOverageDetail>(`/quota/overages/${overageId}/`);
return response.data;
};