feat: Add comprehensive sandbox mode, public API system, and platform support

This commit adds major features for sandbox isolation, public API access, and platform support ticketing.

## Sandbox Mode
- Add sandbox mode toggle for businesses to test features without affecting live data
- Implement schema-based isolation for tenant data (appointments, resources, services)
- Add is_sandbox field filtering for shared models (customers, staff, tickets)
- Create sandbox middleware to detect and set sandbox mode from cookies
- Add sandbox context and hooks for React frontend
- Display sandbox banner when in test mode
- Auto-reload page when switching between live/test modes
- Prevent platform support tickets from being created in sandbox mode

## Public API System
- Full REST API for external integrations with businesses
- API token management with sandbox/live token separation
- Test tokens (ss_test_*) show full plaintext for easy testing
- Live tokens (ss_live_*) are hashed and secure
- Security validation prevents live token plaintext storage
- Comprehensive test suite for token security
- Rate limiting and throttling per token
- Webhook support for real-time event notifications
- Scoped permissions system (read/write per resource type)
- API documentation page with interactive examples
- Token revocation with confirmation modal

## Platform Support
- Dedicated support page for businesses to contact SmoothSchedule
- View all platform support tickets in one place
- Create new support tickets with simplified interface
- Reply to existing tickets with conversation history
- Platform tickets have no admin controls (no priority/category/assignee/status)
- Internal notes hidden for platform tickets (business can't see them)
- Quick help section with links to guides and API docs
- Sandbox warning prevents ticket creation in test mode
- Business ticketing retains full admin controls (priority, assignment, internal notes)

## UI/UX Improvements
- Add notification dropdown with real-time updates
- Staff permissions UI for ticket access control
- Help dropdown in sidebar with Platform Guide, Ticketing Help, API Docs, and Support
- Update sidebar "Contact Support" to "Support" with message icon
- Fix navigation links to use React Router instead of anchor tags
- Remove unused language translations (Japanese, Portuguese, Chinese)

## Technical Details
- Sandbox middleware sets request.sandbox_mode from cookies
- ViewSets filter data by is_sandbox field
- API authentication via custom token auth class
- WebSocket support for real-time ticket updates
- Migration for sandbox fields on User, Tenant, and Ticket models
- Comprehensive documentation in SANDBOX_MODE_IMPLEMENTATION.md

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
poduck
2025-11-28 16:44:06 -05:00
parent 4acea4f876
commit a9719a5fd2
77 changed files with 11407 additions and 2694 deletions

View File

@@ -1,4 +1,27 @@
{
"sandbox": {
"live": "Live",
"test": "Test",
"liveMode": "Live Mode - Production data",
"testMode": "Test Mode - Sandbox data",
"bannerTitle": "TEST MODE",
"bannerDescription": "You are viewing test data. Changes here won't affect your live business.",
"switchToLive": "Switch to Live",
"switching": "Switching...",
"dismiss": "Dismiss"
},
"notifications": {
"title": "Notifications",
"openNotifications": "Open notifications",
"noNotifications": "No notifications yet",
"markAllRead": "Mark all as read",
"clearRead": "Clear read",
"viewAll": "View all",
"justNow": "Just now",
"minutesAgo": "{{count}}m ago",
"hoursAgo": "{{count}}h ago",
"daysAgo": "{{count}}d ago"
},
"common": {
"loading": "Loading...",
"error": "Error",
@@ -65,7 +88,124 @@
"users": "Users",
"support": "Support",
"platformSettings": "Platform Settings",
"tickets": "Tickets"
"tickets": "Tickets",
"help": "Help",
"platformGuide": "Platform Guide",
"ticketingHelp": "Ticketing System",
"apiDocs": "API Docs",
"contactSupport": "Contact Support"
},
"help": {
"guide": {
"title": "Platform Guide",
"subtitle": "Learn how to use SmoothSchedule effectively",
"comingSoon": "Coming Soon",
"comingSoonDesc": "We are working on comprehensive documentation to help you get the most out of SmoothSchedule. Check back soon!"
},
"api": {
"title": "API Reference",
"interactiveExplorer": "Interactive Explorer",
"introduction": "Introduction",
"introDescription": "The SmoothSchedule API is organized around REST. Our API has predictable resource-oriented URLs, accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes.",
"introTestMode": "You can use the SmoothSchedule API in test mode, which doesn't affect your live data. The API key you use determines whether the request is test mode or live mode.",
"baseUrl": "Base URL",
"baseUrlDescription": "All API requests should be made to:",
"sandboxMode": "Sandbox Mode:",
"sandboxModeDescription": "Use the sandbox URL for development and testing. All examples in this documentation use test API keys that work with the sandbox.",
"authentication": "Authentication",
"authDescription": "The SmoothSchedule API uses API keys to authenticate requests. You can view and manage your API keys in your Business Settings.",
"authBearer": "Authentication to the API is performed via Bearer token. Include your API key in the Authorization header of all requests.",
"authWarning": "Your API keys carry many privileges, so be sure to keep them secure. Don't share your secret API keys in publicly accessible areas such as GitHub, client-side code, etc.",
"apiKeyFormat": "API Key Format",
"testKey": "Test/sandbox mode key",
"liveKey": "Live/production mode key",
"authenticatedRequest": "Authenticated Request",
"keepKeysSecret": "Keep your keys secret!",
"keepKeysSecretDescription": "Never expose API keys in client-side code, version control, or public forums.",
"errors": "Errors",
"errorsDescription": "SmoothSchedule uses conventional HTTP response codes to indicate the success or failure of an API request.",
"httpStatusCodes": "HTTP Status Codes",
"errorResponse": "Error Response",
"statusOk": "The request succeeded.",
"statusCreated": "A new resource was created.",
"statusBadRequest": "Invalid request parameters.",
"statusUnauthorized": "Invalid or missing API key.",
"statusForbidden": "The API key lacks required permissions.",
"statusNotFound": "The requested resource doesn't exist.",
"statusConflict": "Resource conflict (e.g., double booking).",
"statusTooManyRequests": "Rate limit exceeded.",
"statusServerError": "Something went wrong on our end.",
"rateLimits": "Rate Limits",
"rateLimitsDescription": "The API implements rate limiting to ensure fair usage and stability.",
"limits": "Limits",
"requestsPerHour": "requests per hour per API key",
"requestsPerMinute": "requests per minute burst limit",
"rateLimitHeaders": "Rate Limit Headers",
"rateLimitHeadersDescription": "Every response includes headers with your current rate limit status.",
"business": "Business",
"businessObject": "The Business object",
"businessObjectDescription": "The Business object represents your business configuration and settings.",
"attributes": "Attributes",
"retrieveBusiness": "Retrieve business",
"retrieveBusinessDescription": "Retrieves the business associated with your API key.",
"requiredScope": "Required scope",
"services": "Services",
"serviceObject": "The Service object",
"serviceObjectDescription": "Services represent the offerings your business provides that customers can book.",
"listServices": "List all services",
"listServicesDescription": "Returns a list of all active services for your business.",
"retrieveService": "Retrieve a service",
"resources": "Resources",
"resourceObject": "The Resource object",
"resourceObjectDescription": "Resources are the bookable entities in your business (staff members, rooms, equipment).",
"listResources": "List all resources",
"retrieveResource": "Retrieve a resource",
"availability": "Availability",
"checkAvailability": "Check availability",
"checkAvailabilityDescription": "Returns available time slots for a given service and date range.",
"parameters": "Parameters",
"appointments": "Appointments",
"appointmentObject": "The Appointment object",
"appointmentObjectDescription": "Appointments represent scheduled bookings between customers and resources.",
"createAppointment": "Create an appointment",
"createAppointmentDescription": "Creates a new appointment booking.",
"retrieveAppointment": "Retrieve an appointment",
"updateAppointment": "Update an appointment",
"cancelAppointment": "Cancel an appointment",
"listAppointments": "List all appointments",
"customers": "Customers",
"customerObject": "The Customer object",
"customerObjectDescription": "Customers are the people who book appointments with your business.",
"createCustomer": "Create a customer",
"retrieveCustomer": "Retrieve a customer",
"updateCustomer": "Update a customer",
"listCustomers": "List all customers",
"webhooks": "Webhooks",
"webhookEvents": "Webhook events",
"webhookEventsDescription": "Webhooks allow you to receive real-time notifications when events occur in your business.",
"eventTypes": "Event types",
"webhookPayload": "Webhook Payload",
"createWebhook": "Create a webhook",
"createWebhookDescription": "Creates a new webhook subscription. The response includes a secret that you'll use to verify webhook signatures.",
"secretOnlyOnce": "The secret is only shown once",
"secretOnlyOnceDescription": ", so save it securely.",
"listWebhooks": "List webhooks",
"deleteWebhook": "Delete a webhook",
"verifySignatures": "Verify signatures",
"verifySignaturesDescription": "Every webhook request includes a signature in the X-Webhook-Signature header. You should verify this signature to ensure the request came from SmoothSchedule.",
"signatureFormat": "Signature format",
"signatureFormatDescription": "The signature header contains two values separated by a dot: a timestamp and the HMAC-SHA256 signature.",
"verificationSteps": "Verification steps",
"verificationStep1": "Extract the timestamp and signature from the header",
"verificationStep2": "Concatenate the timestamp, a dot, and the raw request body",
"verificationStep3": "Compute HMAC-SHA256 using your webhook secret",
"verificationStep4": "Compare the computed signature with the received signature",
"saveYourSecret": "Save your secret!",
"saveYourSecretDescription": "The webhook secret is only returned once when the webhook is created. Store it securely for signature verification.",
"endpoint": "Endpoint",
"request": "Request",
"response": "Response"
}
},
"staff": {
"title": "Staff & Management",
@@ -102,12 +242,16 @@
"ticketDetails": "Ticket Details",
"createTicket": "Create Ticket",
"updateTicket": "Update Ticket",
"comments": "Comments",
"noComments": "No comments yet.",
"internal": "Internal",
"addCommentPlaceholder": "Add a comment...",
"internalComment": "Internal Comment",
"postComment": "Post Comment",
"comments": "Replies",
"noComments": "No replies yet.",
"internal": "Internal Note",
"addCommentPlaceholder": "Write a reply...",
"postComment": "Send Reply",
"replyLabel": "Reply to Customer",
"internalNoteLabel": "Internal Note",
"internalNoteHint": "(Not visible to customer)",
"internalNotePlaceholder": "Add an internal note...",
"addNote": "Add Note",
"tabs": {
"all": "All",
"open": "Open",
@@ -148,7 +292,73 @@
"schedule_change": "Schedule Change",
"equipment": "Equipment Issue",
"other": "Other"
}
},
"sandboxRestriction": "Platform Support Unavailable in Test Mode",
"sandboxRestrictionMessage": "You can only contact SmoothSchedule support in live mode. Please switch to live mode to create a support ticket.",
"status": {
"open": "Open",
"in_progress": "In Progress",
"awaiting_response": "Awaiting Response",
"resolved": "Resolved",
"closed": "Closed"
},
"ticketNumber": "Ticket #{{number}}",
"createdAt": "Created {{date}}"
},
"customerSupport": {
"title": "Support",
"subtitle": "Get help with your appointments and account",
"newRequest": "New Request",
"submitRequest": "Submit Request",
"quickHelp": "Quick Help",
"contactUs": "Contact Us",
"contactUsDesc": "Submit a support request",
"emailUs": "Email Us",
"emailUsDesc": "Get help via email",
"myRequests": "My Support Requests",
"noRequests": "You haven't submitted any support requests yet.",
"submitFirst": "Submit your first request",
"subjectPlaceholder": "Brief summary of your issue",
"descriptionPlaceholder": "Please describe your issue in detail...",
"statusOpen": "Your request has been received. Our team will review it shortly.",
"statusInProgress": "Our team is currently working on your request.",
"statusAwaitingResponse": "We need additional information from you. Please reply below.",
"statusResolved": "Your request has been resolved. Thank you for contacting us!",
"statusClosed": "This ticket has been closed.",
"conversation": "Conversation",
"noRepliesYet": "No replies yet. Our team will respond soon.",
"yourReply": "Your Reply",
"replyPlaceholder": "Type your message here...",
"sendReply": "Send Reply",
"ticketClosedNoReply": "This ticket is closed. If you need further assistance, please open a new support request."
},
"platformSupport": {
"title": "SmoothSchedule Support",
"subtitle": "Get help from the SmoothSchedule team",
"newRequest": "Contact Support",
"quickHelp": "Quick Help",
"platformGuide": "Platform Guide",
"platformGuideDesc": "Learn the basics",
"apiDocs": "API Docs",
"apiDocsDesc": "Integration help",
"contactUs": "Contact Support",
"contactUsDesc": "Get personalized help",
"myRequests": "My Support Requests",
"noRequests": "You haven't submitted any support requests yet.",
"submitFirst": "Submit your first request",
"sandboxWarning": "You are in Test Mode",
"sandboxWarningMessage": "Platform support is only available in Live Mode. Switch to Live Mode to contact SmoothSchedule support.",
"statusOpen": "Your request has been received. Our support team will review it shortly.",
"statusInProgress": "Our support team is currently working on your request.",
"statusAwaitingResponse": "We need additional information from you. Please reply below.",
"statusResolved": "Your request has been resolved. Thank you for contacting SmoothSchedule support!",
"statusClosed": "This ticket has been closed.",
"conversation": "Conversation",
"noRepliesYet": "No replies yet. Our support team will respond soon.",
"yourReply": "Your Reply",
"replyPlaceholder": "Type your message here...",
"sendReply": "Send Reply",
"ticketClosedNoReply": "This ticket is closed. If you need further assistance, please open a new support request."
},
"dashboard": {
"title": "Dashboard",