Files
smoothschedule/frontend/src/types.ts
poduck dcb14503a2 feat: Dashboard redesign, plan permissions, and help docs improvements
Major updates including:
- Customizable dashboard with drag-and-drop widget grid layout
- Plan-based feature locking for plugins and tasks
- Comprehensive help documentation updates across all pages
- Plugin seeding in deployment process for all tenants
- Permission synchronization system for subscription plans
- QuotaOverageModal component and enhanced UX flows

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

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

440 lines
11 KiB
TypeScript

// Domain models based on the Game Plan
// FIX: Added PageComponent types and updated Business interface for website editor feature.
export type PageComponentType = 'HEADING' | 'TEXT' | 'IMAGE' | 'BUTTON' | 'SERVICE' | 'COLUMNS';
export interface PageComponent {
id: string;
type: PageComponentType;
content?: {
text?: string;
level?: 1 | 2 | 3;
src?: string;
alt?: string;
buttonText?: string;
href?: string;
serviceId?: string;
};
children?: PageComponent[][];
}
export interface CustomDomain {
id: number;
domain: string;
is_verified: boolean;
ssl_provisioned: boolean;
is_primary: boolean;
verification_token: string;
dns_txt_record: string;
dns_txt_record_name: string;
created_at: string;
verified_at?: string;
}
export interface PlanPermissions {
sms_reminders: boolean;
webhooks: boolean;
api_access: boolean;
custom_domain: boolean;
white_label: boolean;
custom_oauth: boolean;
plugins: boolean;
tasks: boolean;
export_data: boolean;
video_conferencing: boolean;
two_factor_auth: boolean;
masked_calling: boolean;
pos_system: boolean;
mobile_app: boolean;
}
export interface Business {
id: string;
name: string;
subdomain: string;
primaryColor: string;
secondaryColor: string;
logoUrl?: string;
emailLogoUrl?: string;
logoDisplayMode?: 'logo-only' | 'text-only' | 'logo-and-text'; // How to display branding
timezone?: string; // IANA timezone (e.g., 'America/New_York')
timezoneDisplayMode?: 'business' | 'viewer'; // How times are displayed to users
whitelabelEnabled: boolean;
plan?: 'Free' | 'Professional' | 'Business' | 'Enterprise';
status?: 'Active' | 'Suspended' | 'Trial';
joinedAt?: Date;
resourcesCanReschedule?: boolean;
paymentsEnabled: boolean;
requirePaymentMethodToBook: boolean;
cancellationWindowHours: number;
lateCancellationFeePercent: number;
initialSetupComplete?: boolean;
customDomain?: string;
customDomainVerified?: boolean;
bookingReturnUrl?: string; // URL to redirect customers after booking completion
stripeConnectAccountId?: string;
websitePages?: Record<string, { name: string; content: PageComponent[] }>;
customerDashboardContent?: PageComponent[];
trialStart?: string;
trialEnd?: string;
isTrialActive?: boolean;
isTrialExpired?: boolean;
daysLeftInTrial?: number;
resourceTypes?: ResourceTypeDefinition[]; // Custom resource types
// Platform-controlled permissions
canManageOAuthCredentials?: boolean;
// Plan permissions (what features are available based on subscription)
planPermissions?: PlanPermissions;
}
export type UserRole = 'superuser' | 'platform_manager' | 'platform_support' | 'owner' | 'manager' | 'staff' | 'resource' | 'customer';
export interface NotificationPreferences {
email: boolean;
sms: boolean;
in_app: boolean;
appointment_reminders: boolean;
marketing: boolean;
}
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 User {
id: string | number;
username?: string;
name: string;
email: string;
role: UserRole;
avatarUrl?: string;
phone?: string;
email_verified?: boolean;
two_factor_enabled?: boolean;
timezone?: string;
locale?: string;
notification_preferences?: NotificationPreferences;
can_invite_staff?: boolean;
can_access_tickets?: boolean;
permissions?: Record<string, boolean>;
quota_overages?: QuotaOverage[];
}
export type ResourceType = 'STAFF' | 'ROOM' | 'EQUIPMENT';
export type ResourceTypeCategory = 'STAFF' | 'OTHER';
export interface ResourceTypeDefinition {
id: string;
name: string; // User-facing name like "Stylist", "Massage Therapist", "Treatment Room"
description?: string; // Description of this resource type
category: ResourceTypeCategory; // STAFF (requires staff assignment) or OTHER
isDefault: boolean; // Cannot be deleted
iconName?: string; // Optional icon identifier
}
export interface Resource {
id: string;
name: string;
type: ResourceType; // Legacy field - will be deprecated
typeId?: string; // New field - references ResourceTypeDefinition
userId?: string;
maxConcurrentEvents: number;
savedLaneCount?: number; // Remembered lane count when multilane is disabled
}
export type AppointmentStatus = 'PENDING' | 'CONFIRMED' | 'COMPLETED' | 'CANCELLED' | 'NO_SHOW';
export interface Appointment {
id: string;
resourceId: string | null; // null if unassigned
customerId?: string; // optional for walk-in appointments
customerName: string;
serviceId: string;
startTime: Date; // For MVP, we will assume a specific date
durationMinutes: number;
status: AppointmentStatus;
notes?: string;
}
export interface Blocker {
id: string;
resourceId: string;
startTime: Date;
durationMinutes: number;
title: string;
}
export interface PaymentMethod {
id: string;
brand: 'Visa' | 'Mastercard' | 'Amex';
last4: string;
isDefault: boolean;
}
export interface Customer {
id: string;
name: string;
email: string;
phone: string;
city?: string;
state?: string;
zip?: string;
totalSpend: number;
lastVisit: Date | null;
status: 'Active' | 'Inactive' | 'Blocked';
avatarUrl?: string;
tags?: string[];
userId?: string;
paymentMethods: PaymentMethod[];
}
export interface Service {
id: string;
name: string;
durationMinutes: number;
price: number;
description: string;
displayOrder: number;
photos?: string[];
}
export interface Metric {
label: string;
value: string;
change: string;
trend: 'up' | 'down' | 'neutral';
}
// --- Platform Types ---
export type TicketType = 'PLATFORM' | 'CUSTOMER' | 'STAFF_REQUEST' | 'INTERNAL';
export type TicketStatus = 'OPEN' | 'IN_PROGRESS' | 'RESOLVED' | 'CLOSED' | 'AWAITING_RESPONSE';
export type TicketPriority = 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT';
export type TicketCategory =
| 'BILLING'
| 'TECHNICAL'
| 'FEATURE_REQUEST'
| 'ACCOUNT'
| 'APPOINTMENT'
| 'REFUND'
| 'COMPLAINT'
| 'GENERAL_INQUIRY'
| 'TIME_OFF'
| 'SCHEDULE_CHANGE'
| 'EQUIPMENT'
| 'OTHER';
export interface TicketComment {
id: string;
ticket: string; // Ticket ID
author: string; // User ID
authorEmail: string;
authorFullName: string;
commentText: string;
createdAt: string; // Date string
isInternal: boolean;
}
export interface TicketEmailAddressListItem {
id: number;
display_name: string;
email_address: string;
color: string;
is_active: boolean;
is_default: boolean;
}
export interface Ticket {
id: string;
tenant?: string; // Tenant ID, optional for platform tickets
creator: string; // User ID
creatorEmail: string;
creatorFullName: string;
assignee?: string; // User ID, optional
assigneeEmail?: string;
assigneeFullName?: string;
ticketType: TicketType;
status: TicketStatus;
priority: TicketPriority;
subject: string;
description: string;
category: TicketCategory;
relatedAppointmentId?: string; // Appointment ID, optional
dueAt?: string; // Date string
firstResponseAt?: string; // Date string
isOverdue?: boolean;
createdAt: string; // Date string
updatedAt: string; // Date string
resolvedAt?: string; // Date string
comments?: TicketComment[]; // Nested comments
// External sender info (for tickets from non-registered users via email)
externalEmail?: string;
externalName?: string;
// Source email address (which email address received/sent this ticket)
source_email_address?: TicketEmailAddressListItem;
}
export interface TicketTemplate {
id: string;
tenant?: string; // Tenant ID, optional for platform templates
name: string;
description: string;
ticketType: TicketType;
category: TicketCategory;
defaultPriority: TicketPriority;
subjectTemplate: string;
descriptionTemplate: string;
isActive: boolean;
createdAt: string; // Date string
}
export interface CannedResponse {
id: string;
tenant?: string; // Tenant ID, optional for platform canned responses
title: string;
content: string;
category?: TicketCategory;
isActive: boolean;
useCount: number;
createdBy?: string; // User ID
createdAt: string; // Date string
}
export interface PlatformMetric {
label: string;
value: string;
change: string;
trend: 'up' | 'down' | 'neutral';
color: 'blue' | 'green' | 'purple' | 'orange';
}
// --- OAuth Settings Types ---
export interface OAuthProvider {
id: string;
name: string;
icon: string;
description: string;
}
export interface BusinessOAuthSettings {
enabledProviders: string[];
allowRegistration: boolean;
autoLinkByEmail: boolean;
useCustomCredentials: boolean;
}
export interface BusinessOAuthSettingsResponse {
settings: BusinessOAuthSettings;
availableProviders: OAuthProvider[];
}
// --- OAuth Credentials Types ---
export interface OAuthProviderCredential {
client_id: string;
client_secret: string;
has_secret: boolean;
}
export interface BusinessOAuthCredentialsResponse {
credentials: Record<string, OAuthProviderCredential>;
useCustomCredentials: boolean;
}
// --- Plugin Types ---
export type PluginCategory = 'EMAIL' | 'REPORTS' | 'CUSTOMER' | 'BOOKING' | 'INTEGRATION' | 'AUTOMATION' | 'OTHER';
export interface PluginTemplate {
id: string;
name: string;
description: string;
category: PluginCategory;
version: string;
author: string;
logoUrl?: string;
pluginCode?: string;
rating: number;
ratingCount: number;
installCount: number;
isVerified: boolean;
isFeatured: boolean;
createdAt: string;
updatedAt: string;
}
export interface PluginInstallation {
id: string;
template: string;
templateName: string;
templateDescription: string;
category: PluginCategory;
version: string;
authorName?: string;
logoUrl?: string;
templateVariables?: Record<string, any>;
configValues?: Record<string, any>;
isActive: boolean;
installedAt: string;
hasUpdate: boolean;
rating?: number;
review?: string;
scheduledTaskId?: string;
}
// --- Email Template Types ---
export type EmailTemplateScope = 'BUSINESS' | 'PLATFORM';
export type EmailTemplateCategory =
| 'APPOINTMENT'
| 'REMINDER'
| 'CONFIRMATION'
| 'MARKETING'
| 'NOTIFICATION'
| 'REPORT'
| 'OTHER';
export interface EmailTemplate {
id: string;
name: string;
description: string;
subject: string;
htmlContent: string;
textContent: string;
scope: EmailTemplateScope;
isDefault: boolean;
category: EmailTemplateCategory;
previewContext?: Record<string, any>;
createdBy?: number;
createdByName?: string;
createdAt: string;
updatedAt: string;
}
export interface EmailTemplatePreview {
subject: string;
htmlContent: string;
textContent: string;
forceFooter: boolean;
}
export interface EmailTemplateVariable {
code: string;
description: string;
}
export interface EmailTemplateVariableGroup {
category: string;
items: EmailTemplateVariable[];
}