Major features: - Add drag-and-drop photo gallery to Service create/edit modals - Add Resource Types management section to Settings (CRUD for custom types) - Add edit icon consistency to Resources table (pencil icon in actions) - Improve Services page with drag-to-reorder and customer preview mockup Backend changes: - Add photos JSONField to Service model with migration - Add ResourceType model with category (STAFF/OTHER), description fields - Add ResourceTypeViewSet with CRUD operations - Add service reorder endpoint for display order Frontend changes: - Services page: two-column layout, drag-reorder, photo upload - Settings page: Resource Types tab with full CRUD modal - Resources page: Edit icon in actions column instead of row click - Sidebar: Payments link visibility based on role and paymentsEnabled - Update types.ts with Service.photos and ResourceTypeDefinition Note: Removed photos from ResourceType (kept only for Service) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
229 lines
5.7 KiB
TypeScript
229 lines
5.7 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 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
|
|
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;
|
|
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
|
|
}
|
|
|
|
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 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;
|
|
}
|
|
|
|
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 interface Ticket {
|
|
id: string;
|
|
subject: string;
|
|
businessName: string;
|
|
priority: 'Low' | 'Medium' | 'High' | 'Critical';
|
|
status: 'Open' | 'In Progress' | 'Resolved';
|
|
createdAt: Date;
|
|
}
|
|
|
|
export interface PlatformMetric {
|
|
label: string;
|
|
value: string;
|
|
change: string;
|
|
trend: 'up' | 'down' | 'neutral';
|
|
color: 'blue' | 'green' | 'purple' | 'orange';
|
|
}
|
|
|
|
// --- OAuth Settings Types ---
|
|
|
|
export interface BusinessOAuthSettings {
|
|
enabledProviders: string[];
|
|
allowRegistration: boolean;
|
|
autoLinkByEmail: boolean;
|
|
}
|
|
|
|
export interface BusinessOAuthSettingsResponse {
|
|
businessSettings: BusinessOAuthSettings;
|
|
availableProviders: string[];
|
|
}
|
|
|
|
// --- OAuth Credentials Types ---
|
|
|
|
export interface OAuthProviderCredentials {
|
|
client_id: string;
|
|
client_secret: string;
|
|
team_id?: string; // Apple only
|
|
key_id?: string; // Apple only
|
|
tenant_id?: string; // Microsoft only
|
|
}
|
|
|
|
export interface BusinessOAuthCredentials {
|
|
use_custom_credentials: boolean;
|
|
google?: OAuthProviderCredentials;
|
|
apple?: OAuthProviderCredentials;
|
|
facebook?: OAuthProviderCredentials;
|
|
linkedin?: OAuthProviderCredentials;
|
|
microsoft?: OAuthProviderCredentials;
|
|
twitter?: OAuthProviderCredentials;
|
|
twitch?: OAuthProviderCredentials;
|
|
} |