Files
Bookra/apps/backend/internal/domain/models.go
T
Tomas DvorakandDevin 76e45d4178 feat(metrics): per-staff performance view in dashboard
GET /v1/metrics/staff returns bookings, completed, cancelled,
utilization, and revenue per staff member over a trailing window
(default 30d, max 365d). Appointment bookings only — class sessions
have no staff assignment. All active staff appear, including
zero-booking rows.

Staff page gains a localized Performance panel; demo mode seeds
trailing-window bookings and matching metric rows.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 16:34:45 +02:00

751 lines
27 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"`
ClassSessionID *string `json:"classSessionId,omitempty"`
WaitlistPosition int `json:"waitlistPosition,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"`
// WaitlistCount is the number of customers queued for a free seat.
WaitlistCount int `json:"waitlistCount"`
}
// SessionRosterEntry is one booking on a class session roster. WaitlistPosition
// is the 1-based queue spot for waitlisted entries; zero for confirmed ones.
type SessionRosterEntry struct {
BookingID string `json:"bookingId"`
Reference string `json:"reference"`
CustomerName string `json:"customerName"`
CustomerEmail string `json:"customerEmail"`
CustomerPhone string `json:"customerPhone,omitempty"`
Status string `json:"status"`
WaitlistPosition int `json:"waitlistPosition,omitempty"`
CreatedAt time.Time `json:"createdAt"`
}
// SessionRoster is the dashboard attendee view of a class session: confirmed
// seats plus the waitlist queue in FIFO order.
type SessionRoster struct {
SessionID string `json:"sessionId"`
TemplateTitle string `json:"templateTitle"`
LocationName string `json:"locationName,omitempty"`
StartsAt time.Time `json:"startsAt"`
EndsAt time.Time `json:"endsAt"`
Capacity int `json:"capacity"`
Confirmed []SessionRosterEntry `json:"confirmed"`
Waitlist []SessionRosterEntry `json:"waitlist"`
}
// PromoteBookingRequest controls manual waitlist promotion. AllowOverCapacity
// lets a business deliberately overbook a session; without it the request fails
// when no free seat remains.
type PromoteBookingRequest struct {
AllowOverCapacity bool `json:"allowOverCapacity,omitempty"`
}
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"`
TenantSlug string `json:"tenantSlug,omitempty"`
ServiceID *string `json:"serviceId,omitempty"`
StaffID *string `json:"staffId,omitempty"`
// ClassSessionID identifies session-bound bookings; they cannot be
// rescheduled to arbitrary times (see RescheduleBooking).
ClassSessionID *string `json:"classSessionId,omitempty"`
StartsAt time.Time `json:"startsAt"`
EndsAt time.Time `json:"endsAt"`
Location string `json:"location"`
Status string `json:"status"`
Notes string `json:"notes,omitempty"`
// WaitlistPosition is the 1-based queue spot for waitlisted class
// bookings; zero for any other status.
WaitlistPosition int `json:"waitlistPosition,omitempty"`
}
type RescheduleBookingRequest struct {
NewStartsAt string `json:"newStartsAt" binding:"required"` // RFC3339
NewEndsAt string `json:"newEndsAt" binding:"required"` // RFC3339
Reason string `json:"reason,omitempty"`
}
// SessionMoveTarget is an upcoming session of the same class template a
// customer can move their booking to. Remaining is the free confirmed seats;
// negative means a queue would form.
type SessionMoveTarget struct {
SessionID string `json:"sessionId"`
Title string `json:"title"`
StartsAt time.Time `json:"startsAt"`
EndsAt time.Time `json:"endsAt"`
Location string `json:"location,omitempty"`
Capacity int32 `json:"capacity"`
ConfirmedCount int `json:"confirmedCount"`
WaitlistCount int `json:"waitlistCount"`
Remaining int `json:"remaining"`
}
type MoveBookingRequest struct {
ClassSessionID string `json:"classSessionId" binding:"required"`
}
type MoveBookingResponse struct {
Status string `json:"status"` // confirmed | waitlisted
}
type CancelBookingRequest struct {
Reason string `json:"reason,omitempty"`
}
// ActivityEvent is a per-tenant audit entry: who changed what. ActorKind is
// staff | customer | system; Action is a dotted verb like booking.created.
type ActivityEvent struct {
ID string `json:"id"`
ActorKind string `json:"actorKind"`
ActorName string `json:"actorName,omitempty"`
Action string `json:"action"`
EntityType string `json:"entityType"`
EntityID string `json:"entityId,omitempty"`
Summary string `json:"summary"`
CreatedAt time.Time `json:"createdAt"`
}
type ActivityFeedResponse struct {
Events []ActivityEvent `json:"events"`
}
// StaffMetric aggregates appointment workload for one staff member over a
// trailing window. Class bookings carry no staff assignment and are excluded.
type StaffMetric struct {
StaffID string `json:"staffId"`
DisplayName string `json:"displayName"`
Bookings int `json:"bookings"`
Completed int `json:"completed"`
Cancelled int `json:"cancelled"`
UtilizationPercent int `json:"utilizationPercent"`
RevenueCents int64 `json:"revenueCents"`
}
type StaffMetricsResponse struct {
From time.Time `json:"from"`
To time.Time `json:"to"`
Staff []StaffMetric `json:"staff"`
}
// ============================================
// 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"`
}