mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-10-28 15:51:15 +00:00
The reminder pipeline fired regardless of what happened to the booking after creation: cancelled customers got reminded about bookings that weren't happening, reschedules kept the old reminder time, and waitlisted bookings armed jobs too. Pending jobs are now cancelled on both cancel paths, deleted and re-armed on reschedule, and dispatch joins bookings.status to skip anything not confirmed. Reschedule also gained the same availability-window and blocked-day validation as booking creation, closing a 3am-reschedule hole via the manage URL. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
647 lines
22 KiB
Go
647 lines
22 KiB
Go
package domain
|
|
|
|
import "time"
|
|
|
|
type Principal struct {
|
|
Subject string `json:"subject"`
|
|
Email string `json:"email,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
Role string `json:"role"`
|
|
}
|
|
|
|
type DashboardKPI struct {
|
|
Code string `json:"code"`
|
|
Label string `json:"label"`
|
|
Value string `json:"value"`
|
|
Change string `json:"change,omitempty"`
|
|
Trend string `json:"trend,omitempty"`
|
|
}
|
|
|
|
type UpcomingBooking struct {
|
|
ID string `json:"id"`
|
|
Reference string `json:"reference"`
|
|
CustomerName string `json:"customerName"`
|
|
CustomerEmail string `json:"customerEmail"`
|
|
Service string `json:"service,omitempty"`
|
|
Location string `json:"location,omitempty"`
|
|
StaffName string `json:"staffName,omitempty"`
|
|
Notes string `json:"notes,omitempty"`
|
|
StartsAt time.Time `json:"startsAt"`
|
|
EndsAt time.Time `json:"endsAt"`
|
|
Status string `json:"status"`
|
|
Label string `json:"label,omitempty"`
|
|
}
|
|
|
|
type WidgetSnippet struct {
|
|
Kind string `json:"kind"`
|
|
Code string `json:"code"`
|
|
}
|
|
|
|
type TrackingStatus struct {
|
|
Provider string `json:"provider"`
|
|
Connected bool `json:"connected"`
|
|
SiteID string `json:"siteId,omitempty"`
|
|
Message string `json:"message,omitempty"`
|
|
}
|
|
|
|
type DashboardSummary struct {
|
|
TenantName string `json:"tenantName"`
|
|
TenantSlug string `json:"tenantSlug"`
|
|
Locale string `json:"locale"`
|
|
Timezone string `json:"timezone"`
|
|
PlanCode string `json:"planCode"`
|
|
PublicBookingURL string `json:"publicBookingUrl"`
|
|
SetupCompletion int `json:"setupCompletion"`
|
|
KPIs []DashboardKPI `json:"kpis"`
|
|
UpcomingBookings []UpcomingBooking `json:"upcomingBookings"`
|
|
AllBookings []UpcomingBooking `json:"allBookings"`
|
|
WidgetSnippets []WidgetSnippet `json:"widgetSnippets"`
|
|
Tracking TrackingStatus `json:"tracking"`
|
|
}
|
|
|
|
type BrandProfile struct {
|
|
Name string `json:"name"`
|
|
SiteURL string `json:"siteUrl,omitempty"`
|
|
LogoURL string `json:"logoUrl,omitempty"`
|
|
PrimaryColor string `json:"primaryColor,omitempty"`
|
|
}
|
|
|
|
type TenantBootstrap struct {
|
|
TenantID string `json:"tenantId"`
|
|
TenantName string `json:"tenantName"`
|
|
TenantSlug string `json:"tenantSlug,omitempty"`
|
|
Preset string `json:"preset"`
|
|
Locale string `json:"locale"`
|
|
Timezone string `json:"timezone"`
|
|
PlanCode string `json:"planCode,omitempty"`
|
|
OnboardingCompleted bool `json:"onboardingCompleted"`
|
|
Brand BrandProfile `json:"brand"`
|
|
CurrentUser Principal `json:"currentUser"`
|
|
}
|
|
|
|
type TeamInviteRequest struct {
|
|
Email string `json:"email"`
|
|
Role string `json:"role,omitempty"`
|
|
}
|
|
|
|
type AvailabilityBlockRequest struct {
|
|
DayOfWeek int `json:"dayOfWeek"`
|
|
StartsLocal string `json:"startsLocal"`
|
|
EndsLocal string `json:"endsLocal"`
|
|
Busy bool `json:"busy,omitempty"`
|
|
}
|
|
|
|
type BookingDefaultsRequest struct {
|
|
ServiceName string `json:"serviceName"`
|
|
DurationMinutes int `json:"durationMinutes"`
|
|
BufferBeforeMinutes int `json:"bufferBeforeMinutes"`
|
|
BufferAfterMinutes int `json:"bufferAfterMinutes"`
|
|
CancelWindowHours int `json:"cancelWindowHours"`
|
|
}
|
|
|
|
type OnboardTenantRequest struct {
|
|
Name string `json:"name"`
|
|
Slug string `json:"slug"`
|
|
Preset string `json:"preset"`
|
|
Locale string `json:"locale"`
|
|
Timezone string `json:"timezone"`
|
|
Brand BrandProfile `json:"brand"`
|
|
LocationName string `json:"locationName"`
|
|
BookingDefaults BookingDefaultsRequest `json:"bookingDefaults"`
|
|
AvailabilityBlocks []AvailabilityBlockRequest `json:"availabilityBlocks"`
|
|
TeamInvites []TeamInviteRequest `json:"teamInvites"`
|
|
}
|
|
|
|
type TimeSlot struct {
|
|
ServiceID *string `json:"serviceId,omitempty"`
|
|
ClassSessionID *string `json:"classSessionId,omitempty"`
|
|
StaffID *string `json:"staffId,omitempty"`
|
|
StaffName *string `json:"staffName,omitempty"`
|
|
LocationID *string `json:"locationId,omitempty"`
|
|
StartsAt string `json:"startsAt"`
|
|
EndsAt string `json:"endsAt"`
|
|
Mode string `json:"mode"`
|
|
Label string `json:"label"`
|
|
RemainingCapacity *int32 `json:"remainingCapacity,omitempty"`
|
|
}
|
|
|
|
type PublicAvailability struct {
|
|
TenantSlug string `json:"tenantSlug"`
|
|
Timezone string `json:"timezone"`
|
|
Locale string `json:"locale"`
|
|
Slots []TimeSlot `json:"slots"`
|
|
}
|
|
|
|
type PublicService struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
DurationMinutes int `json:"durationMinutes"`
|
|
PriceCents int `json:"priceCents"`
|
|
}
|
|
|
|
type PublicStaff struct {
|
|
ID string `json:"id"`
|
|
DisplayName string `json:"displayName"`
|
|
}
|
|
|
|
type PublicCatalog struct {
|
|
TenantSlug string `json:"tenantSlug"`
|
|
Services []PublicService `json:"services"`
|
|
Staff []PublicStaff `json:"staff"`
|
|
}
|
|
|
|
type CreateBookingRequest struct {
|
|
TenantSlug string `json:"tenantSlug"`
|
|
BookingMode string `json:"bookingMode"`
|
|
ServiceID *string `json:"serviceId,omitempty"`
|
|
ClassSessionID *string `json:"classSessionId,omitempty"`
|
|
StaffID *string `json:"staffId,omitempty"`
|
|
LocationID *string `json:"locationId,omitempty"`
|
|
CustomerName string `json:"customerName"`
|
|
CustomerEmail string `json:"customerEmail"`
|
|
CustomerPhone string `json:"customerPhone,omitempty"`
|
|
Notes string `json:"notes"`
|
|
StartsAt string `json:"startsAt"`
|
|
EndsAt string `json:"endsAt"`
|
|
}
|
|
|
|
type CreateBookingResponse struct {
|
|
BookingID string `json:"bookingId"`
|
|
Reference string `json:"reference"`
|
|
Status string `json:"status"`
|
|
ManageURL string `json:"manageUrl"`
|
|
}
|
|
|
|
type PlanEntitlements struct {
|
|
MaxLocations int `json:"maxLocations"`
|
|
MaxStaff int `json:"maxStaff"`
|
|
MaxBookingsMonth int `json:"maxBookingsMonth"` // -1 = unlimited
|
|
EmailReminders bool `json:"emailReminders"`
|
|
AdvancedReporting bool `json:"advancedReporting"`
|
|
WidgetEmbedding bool `json:"widgetEmbedding"`
|
|
UmamiTracking bool `json:"umamiTracking"`
|
|
APIAccess bool `json:"apiAccess"`
|
|
DedicatedManager bool `json:"dedicatedManager"`
|
|
SMSAvailable bool `json:"smsAvailable"`
|
|
}
|
|
|
|
type PlanPricing struct {
|
|
MonthlyAmountCents int `json:"monthlyAmountCents"`
|
|
YearlyAmountCents int `json:"yearlyAmountCents"`
|
|
MonthlyFormatted string `json:"monthlyFormatted"`
|
|
YearlyFormatted string `json:"yearlyFormatted"`
|
|
YearlySavings string `json:"yearlySavings"`
|
|
YearlySavingsPercent int `json:"yearlySavingsPercent"`
|
|
}
|
|
|
|
type PlanDisplayPrice struct {
|
|
Currency string `json:"currency"`
|
|
AmountCents int `json:"amountCents"`
|
|
Formatted string `json:"formatted"`
|
|
YearlyAmountCents int `json:"yearlyAmountCents,omitempty"`
|
|
YearlyFormatted string `json:"yearlyFormatted,omitempty"`
|
|
YearlySavings string `json:"yearlySavings,omitempty"`
|
|
YearlySavingsPercent int `json:"yearlySavingsPercent,omitempty"`
|
|
}
|
|
|
|
type SubscriptionSnapshot struct {
|
|
TenantID string `json:"tenantId"`
|
|
Provider string `json:"provider"`
|
|
CustomerID string `json:"customerId"`
|
|
SubscriptionID string `json:"subscriptionId"`
|
|
Status string `json:"status"`
|
|
PlanCode string `json:"planCode"`
|
|
Currency string `json:"currency"`
|
|
PriceID string `json:"priceId"`
|
|
CancelAtPeriodEnd bool `json:"cancelAtPeriodEnd"`
|
|
CurrentPeriodStart *time.Time `json:"currentPeriodStart,omitempty"`
|
|
CurrentPeriodEnd *time.Time `json:"currentPeriodEnd,omitempty"`
|
|
PaymentMethodBrand string `json:"paymentMethodBrand,omitempty"`
|
|
PaymentMethodLast4 string `json:"paymentMethodLast4,omitempty"`
|
|
Entitlements PlanEntitlements `json:"entitlements"`
|
|
DisplayPrices []PlanDisplayPrice `json:"displayPrices"`
|
|
TrialDays int `json:"trialDays"`
|
|
LastSyncedAt *time.Time `json:"lastSyncedAt,omitempty"`
|
|
CheckoutURLAvailable bool `json:"checkoutUrlAvailable"`
|
|
SyncAvailable bool `json:"syncAvailable"`
|
|
PortalAvailable bool `json:"portalAvailable"`
|
|
}
|
|
|
|
type CheckoutSessionRequest struct {
|
|
PlanCode string `json:"planCode"`
|
|
Currency string `json:"currency,omitempty"`
|
|
BillingInterval string `json:"billingInterval,omitempty"` // "monthly" or "yearly", defaults to "monthly"
|
|
}
|
|
|
|
type CheckoutLaunchResponse struct {
|
|
CheckoutURL string `json:"checkoutUrl,omitempty"`
|
|
SuccessRedirectURL string `json:"successRedirectUrl,omitempty"`
|
|
CancelRedirectURL string `json:"cancelRedirectUrl,omitempty"`
|
|
}
|
|
|
|
type PortalSessionResponse struct {
|
|
URL string `json:"url"`
|
|
}
|
|
|
|
type DispatchReminderJobsRequest struct {
|
|
Limit int `json:"limit,omitempty"`
|
|
}
|
|
|
|
type DispatchReminderJobsResponse struct {
|
|
ProcessedCount int `json:"processedCount"`
|
|
SentCount int `json:"sentCount"`
|
|
FailedCount int `json:"failedCount"`
|
|
SkippedCount int `json:"skippedCount"`
|
|
}
|
|
|
|
// ============================================
|
|
// LOCATION / ZONE MODELS
|
|
// ============================================
|
|
|
|
type Location struct {
|
|
ID string `json:"id"`
|
|
TenantID string `json:"tenantId"`
|
|
Name string `json:"name"`
|
|
Type string `json:"type"` // room, private, hall, etc.
|
|
Capacity int `json:"capacity"`
|
|
Timezone string `json:"timezone"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
type DashboardBookingRequest struct {
|
|
CustomerName string `json:"customerName" binding:"required"`
|
|
CustomerEmail string `json:"customerEmail" binding:"required,email"`
|
|
CustomerPhone string `json:"customerPhone,omitempty"`
|
|
Service string `json:"service,omitempty"`
|
|
StartsAt string `json:"startsAt" binding:"required"`
|
|
Notes string `json:"notes,omitempty"`
|
|
}
|
|
|
|
type CreateLocationRequest struct {
|
|
Name string `json:"name" binding:"required"`
|
|
Type string `json:"type" binding:"required"`
|
|
Capacity int `json:"capacity"`
|
|
}
|
|
|
|
type UpdateLocationRequest struct {
|
|
Name string `json:"name,omitempty"`
|
|
Type string `json:"type,omitempty"`
|
|
Capacity int `json:"capacity,omitempty"`
|
|
}
|
|
|
|
// ============================================
|
|
// SERVICE MODELS
|
|
// ============================================
|
|
|
|
type Service struct {
|
|
ID string `json:"id"`
|
|
TenantID string `json:"tenantId"`
|
|
Name string `json:"name"`
|
|
DurationMinutes int `json:"durationMinutes"`
|
|
BufferBeforeMinutes int `json:"bufferBeforeMinutes"`
|
|
BufferAfterMinutes int `json:"bufferAfterMinutes"`
|
|
PriceCents int `json:"priceCents"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
type CreateServiceRequest struct {
|
|
Name string `json:"name" binding:"required"`
|
|
DurationMinutes int `json:"durationMinutes" binding:"required"`
|
|
BufferBeforeMinutes int `json:"bufferBeforeMinutes,omitempty"`
|
|
BufferAfterMinutes int `json:"bufferAfterMinutes,omitempty"`
|
|
PriceCents int `json:"priceCents,omitempty"`
|
|
}
|
|
|
|
type UpdateServiceRequest struct {
|
|
Name *string `json:"name,omitempty"`
|
|
DurationMinutes *int `json:"durationMinutes,omitempty"`
|
|
BufferBeforeMinutes *int `json:"bufferBeforeMinutes,omitempty"`
|
|
BufferAfterMinutes *int `json:"bufferAfterMinutes,omitempty"`
|
|
PriceCents *int `json:"priceCents,omitempty"`
|
|
}
|
|
|
|
// ============================================
|
|
// STAFF MODELS
|
|
// ============================================
|
|
|
|
type StaffMember struct {
|
|
ID string `json:"id"`
|
|
TenantID string `json:"tenantId"`
|
|
LocationID *string `json:"locationId,omitempty"`
|
|
DisplayName string `json:"displayName"`
|
|
Active bool `json:"active"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
type CreateStaffRequest struct {
|
|
DisplayName string `json:"displayName" binding:"required"`
|
|
LocationID *string `json:"locationId,omitempty"`
|
|
Active *bool `json:"active,omitempty"` // defaults to true
|
|
}
|
|
|
|
type UpdateStaffRequest struct {
|
|
DisplayName *string `json:"displayName,omitempty"`
|
|
LocationID *string `json:"locationId,omitempty"` // empty string clears the assignment
|
|
Active *bool `json:"active,omitempty"`
|
|
}
|
|
|
|
// ============================================
|
|
// BLOCKED DAYS / AVAILABILITY EXCEPTION MODELS
|
|
// ============================================
|
|
|
|
type BlockedDay struct {
|
|
ID string `json:"id"`
|
|
TenantID string `json:"tenantId"`
|
|
Date time.Time `json:"date"`
|
|
Reason string `json:"reason"`
|
|
Type string `json:"type"` // full, partial
|
|
StaffID *string `json:"staffId,omitempty"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
type CreateBlockedDayRequest struct {
|
|
Date string `json:"date" binding:"required"` // RFC3339
|
|
Reason string `json:"reason" binding:"required"`
|
|
Type string `json:"type" binding:"required"` // full, partial
|
|
StaffID *string `json:"staffId,omitempty"`
|
|
}
|
|
|
|
type UpdateBlockedDayRequest struct {
|
|
Reason string `json:"reason,omitempty"`
|
|
Type string `json:"type,omitempty"`
|
|
}
|
|
|
|
// ============================================
|
|
// CLASS MODELS
|
|
// ============================================
|
|
|
|
type ClassTemplate struct {
|
|
ID string `json:"id"`
|
|
TenantID string `json:"tenantId"`
|
|
Title string `json:"title"`
|
|
DurationMinutes int `json:"durationMinutes"`
|
|
Capacity int `json:"capacity"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
type UpsertClassTemplateRequest struct {
|
|
Title string `json:"title" binding:"required"`
|
|
DurationMinutes int `json:"durationMinutes" binding:"required"`
|
|
Capacity int `json:"capacity" binding:"required"`
|
|
}
|
|
|
|
type ClassSession struct {
|
|
ID string `json:"id"`
|
|
TenantID string `json:"tenantId"`
|
|
TemplateID string `json:"templateId"`
|
|
TemplateTitle string `json:"templateTitle"`
|
|
LocationID *string `json:"locationId,omitempty"`
|
|
LocationName string `json:"locationName,omitempty"`
|
|
StartsAt time.Time `json:"startsAt"`
|
|
EndsAt time.Time `json:"endsAt"`
|
|
Capacity int `json:"capacity"`
|
|
BookedCount int `json:"bookedCount"`
|
|
}
|
|
|
|
type CreateClassSessionRequest struct {
|
|
TemplateID string `json:"templateId" binding:"required"`
|
|
LocationID *string `json:"locationId,omitempty"`
|
|
StartsAt string `json:"startsAt" binding:"required"` // RFC3339
|
|
Capacity *int `json:"capacity,omitempty"`
|
|
}
|
|
|
|
// ============================================
|
|
// CUSTOMER MODELS
|
|
// ============================================
|
|
|
|
type Customer struct {
|
|
ID string `json:"id"`
|
|
TenantID string `json:"tenantId"`
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
Phone *string `json:"phone,omitempty"`
|
|
Status string `json:"status"` // active, inactive, vip
|
|
BookingsCount int `json:"bookingsCount"`
|
|
LastBookingAt *time.Time `json:"lastBookingAt,omitempty"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
Notes string `json:"notes,omitempty"`
|
|
}
|
|
|
|
type CreateCustomerRequest struct {
|
|
Name string `json:"name" binding:"required"`
|
|
Email string `json:"email" binding:"required,email"`
|
|
Phone *string `json:"phone,omitempty"`
|
|
Status string `json:"status,omitempty"` // defaults to active
|
|
Notes string `json:"notes,omitempty"`
|
|
}
|
|
|
|
type UpdateCustomerRequest struct {
|
|
Name string `json:"name,omitempty"`
|
|
Email string `json:"email,omitempty"`
|
|
Phone *string `json:"phone,omitempty"`
|
|
Status string `json:"status,omitempty"`
|
|
Notes string `json:"notes,omitempty"`
|
|
}
|
|
|
|
// ============================================
|
|
// CUSTOMER BOOKING MANAGEMENT MODELS
|
|
// ============================================
|
|
|
|
type CustomerBookingView struct {
|
|
Reference string `json:"reference"`
|
|
CustomerName string `json:"customerName"`
|
|
CustomerEmail string `json:"customerEmail"`
|
|
Service string `json:"service"`
|
|
BusinessName string `json:"businessName"`
|
|
StartsAt time.Time `json:"startsAt"`
|
|
EndsAt time.Time `json:"endsAt"`
|
|
Location string `json:"location"`
|
|
Status string `json:"status"`
|
|
Notes string `json:"notes,omitempty"`
|
|
}
|
|
|
|
type RescheduleBookingRequest struct {
|
|
NewStartsAt string `json:"newStartsAt" binding:"required"` // RFC3339
|
|
NewEndsAt string `json:"newEndsAt" binding:"required"` // RFC3339
|
|
Reason string `json:"reason,omitempty"`
|
|
}
|
|
|
|
type CancelBookingRequest struct {
|
|
Reason string `json:"reason,omitempty"`
|
|
}
|
|
|
|
// ============================================
|
|
// ADMIN MODELS
|
|
// ============================================
|
|
|
|
type AdminDashboardStats struct {
|
|
TotalTenants int64 `json:"totalTenants"`
|
|
TotalUsers int64 `json:"totalUsers"`
|
|
ActiveSubscriptions int64 `json:"activeSubscriptions"`
|
|
TrialSubscriptions int64 `json:"trialSubscriptions"`
|
|
BookingsThisMonth int64 `json:"bookingsThisMonth"`
|
|
RevenueThisMonthCents int64 `json:"revenueThisMonthCents"`
|
|
}
|
|
|
|
type AdminTenantList struct {
|
|
Total int `json:"total"`
|
|
Page int `json:"page"`
|
|
PageSize int `json:"pageSize"`
|
|
Tenants []AdminTenant `json:"tenants"`
|
|
}
|
|
|
|
type AdminTenant struct {
|
|
ID string `json:"id"`
|
|
Slug string `json:"slug"`
|
|
Name string `json:"name"`
|
|
PlanCode string `json:"planCode"`
|
|
SubscriptionStatus string `json:"subscriptionStatus"`
|
|
BillingProvider string `json:"billingProvider"`
|
|
}
|
|
|
|
type AdminUserList struct {
|
|
Total int `json:"total"`
|
|
Page int `json:"page"`
|
|
PageSize int `json:"pageSize"`
|
|
Users []AdminUser `json:"users"`
|
|
}
|
|
|
|
type AdminUser struct {
|
|
ID string `json:"id"`
|
|
Email string `json:"email"`
|
|
Name string `json:"name,omitempty"`
|
|
EmailVerified bool `json:"emailVerified"`
|
|
Provider string `json:"provider"`
|
|
Role string `json:"role"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
type AdminLoginRequest struct {
|
|
Email string `json:"email" binding:"required,email"`
|
|
Key string `json:"key" binding:"required"`
|
|
}
|
|
|
|
type UpdateUserRoleRequest struct {
|
|
Role string `json:"role" binding:"required,oneof=user admin superadmin"`
|
|
}
|
|
|
|
// ============================================
|
|
// WORKING HOURS MODELS
|
|
// ============================================
|
|
|
|
type WorkingHours struct {
|
|
DayOfWeek int `json:"dayOfWeek"` // 0=Sunday, 1=Monday, etc.
|
|
Open string `json:"open"` // HH:MM format
|
|
Close string `json:"close"` // HH:MM format
|
|
IsOpen bool `json:"isOpen"`
|
|
}
|
|
|
|
type UpdateWorkingHoursRequest struct {
|
|
Open string `json:"open,omitempty"`
|
|
Close string `json:"close,omitempty"`
|
|
IsOpen *bool `json:"isOpen,omitempty"`
|
|
}
|
|
|
|
// ============================================
|
|
// EMAIL TEMPLATE MODELS
|
|
// ============================================
|
|
|
|
type EmailTemplate struct {
|
|
ID string `json:"id"`
|
|
TenantID string `json:"tenantId"`
|
|
Type string `json:"type"` // booking_confirmation, reminder, cancellation, etc.
|
|
Subject string `json:"subject"`
|
|
BodyHTML string `json:"bodyHtml"`
|
|
BodyText string `json:"bodyText"`
|
|
IsEnabled bool `json:"isEnabled"`
|
|
}
|
|
|
|
type UpdateBrandRequest struct {
|
|
Name string `json:"name,omitempty"`
|
|
SiteURL string `json:"siteUrl,omitempty"`
|
|
LogoURL string `json:"logoUrl,omitempty"`
|
|
PrimaryColor string `json:"primaryColor" binding:"required"`
|
|
}
|
|
|
|
type UpdateEmailTemplateRequest struct {
|
|
Type string `json:"type" binding:"required"`
|
|
Subject string `json:"subject"`
|
|
Body string `json:"body"`
|
|
IsEnabled *bool `json:"isEnabled,omitempty"`
|
|
}
|
|
|
|
type SendEmailRequest struct {
|
|
To string `json:"to" binding:"required,email"`
|
|
Subject string `json:"subject" binding:"required"`
|
|
Body string `json:"body" binding:"required"`
|
|
Data map[string]string `json:"data,omitempty"` // Template variables
|
|
}
|
|
|
|
type EmailNotification struct {
|
|
ID string `json:"id"`
|
|
TenantID string `json:"tenantId"`
|
|
BookingID string `json:"bookingId,omitempty"`
|
|
Channel string `json:"channel"` // email, sms
|
|
Type string `json:"type"` // confirmation, reminder, cancellation
|
|
Recipient string `json:"recipient"`
|
|
Status string `json:"status"` // pending, sent, failed
|
|
SentAt *time.Time `json:"sentAt,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
// ============================================
|
|
// SMS MODELS
|
|
// ============================================
|
|
|
|
type SMSSettings struct {
|
|
Enabled bool `json:"enabled"`
|
|
SenderName string `json:"senderName,omitempty"`
|
|
MonthlyLimit int `json:"monthlyLimit,omitempty"`
|
|
MessagesSent int `json:"messagesSent"`
|
|
TotalCostCents int `json:"totalCostCents"`
|
|
Available bool `json:"available"`
|
|
}
|
|
|
|
type UpdateSMSSettingsRequest struct {
|
|
Enabled bool `json:"enabled"`
|
|
SenderName string `json:"senderName,omitempty"`
|
|
MonthlyLimit int `json:"monthlyLimit,omitempty"`
|
|
}
|
|
|
|
type SendSMSRequest struct {
|
|
To string `json:"to" binding:"required"`
|
|
Body string `json:"body" binding:"required,max=1000"`
|
|
}
|
|
|
|
type SendSMSResponse struct {
|
|
LogID string `json:"logId"`
|
|
MessageID string `json:"messageId,omitempty"`
|
|
RequestID string `json:"requestId,omitempty"`
|
|
Status string `json:"status"`
|
|
CostCents int `json:"costCents"`
|
|
}
|
|
|
|
type SMSUsageLog struct {
|
|
ID string `json:"id"`
|
|
RecipientPhone string `json:"recipientPhone"`
|
|
MessageBody string `json:"messageBody,omitempty"`
|
|
Status string `json:"status"`
|
|
CostCents int `json:"costCents"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
type SMSUsageReport struct {
|
|
YearMonth string `json:"yearMonth"`
|
|
MessageCount int `json:"messageCount"`
|
|
TotalCostCents int `json:"totalCostCents"`
|
|
StripeInvoiceID string `json:"stripeInvoiceId,omitempty"`
|
|
InvoiceSentAt *time.Time `json:"invoiceSentAt,omitempty"`
|
|
}
|
|
|
|
type SMSInvoiceBatchResponse struct {
|
|
YearMonth string `json:"yearMonth"`
|
|
ProcessedCount int `json:"processedCount"`
|
|
FailedCount int `json:"failedCount"`
|
|
}
|