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

@@ -52,6 +52,7 @@
"scheduler": "Agenda",
"customers": "Clientes",
"resources": "Recursos",
"services": "Servicios",
"payments": "Pagos",
"messages": "Mensajes",
"staff": "Personal",
@@ -61,7 +62,124 @@
"businesses": "Negocios",
"users": "Usuarios",
"support": "Soporte",
"platformSettings": "Configuración de Plataforma"
"platformSettings": "Configuración de Plataforma",
"tickets": "Tickets",
"help": "Ayuda",
"platformGuide": "Guía de Plataforma",
"ticketingHelp": "Sistema de Tickets",
"apiDocs": "Documentación API"
},
"help": {
"guide": {
"title": "Guía de Plataforma",
"subtitle": "Aprende a usar SmoothSchedule de manera efectiva",
"comingSoon": "Próximamente",
"comingSoonDesc": "Estamos trabajando en documentación completa para ayudarte a aprovechar al máximo SmoothSchedule. ¡Vuelve pronto!"
},
"api": {
"title": "Referencia de API",
"interactiveExplorer": "Explorador Interactivo",
"introduction": "Introducción",
"introDescription": "La API de SmoothSchedule está organizada según REST. Nuestra API tiene URLs predecibles orientadas a recursos, acepta cuerpos de solicitud codificados en JSON, devuelve respuestas codificadas en JSON y utiliza códigos de respuesta HTTP estándar.",
"introTestMode": "Puedes usar la API de SmoothSchedule en modo de prueba, que no afecta tus datos en vivo. La clave API que uses determina si la solicitud es en modo de prueba o en vivo.",
"baseUrl": "URL Base",
"baseUrlDescription": "Todas las solicitudes API deben realizarse a:",
"sandboxMode": "Modo Sandbox:",
"sandboxModeDescription": "Usa la URL de sandbox para desarrollo y pruebas. Todos los ejemplos en esta documentación usan claves API de prueba que funcionan con el sandbox.",
"authentication": "Autenticación",
"authDescription": "La API de SmoothSchedule usa claves API para autenticar solicitudes. Puedes ver y gestionar tus claves API en la Configuración de tu Negocio.",
"authBearer": "La autenticación a la API se realiza mediante token Bearer. Incluye tu clave API en el encabezado Authorization de todas las solicitudes.",
"authWarning": "Tus claves API tienen muchos privilegios, así que asegúrate de mantenerlas seguras. No compartas tus claves API secretas en áreas públicamente accesibles como GitHub, código del lado del cliente, etc.",
"apiKeyFormat": "Formato de Clave API",
"testKey": "Clave de modo prueba/sandbox",
"liveKey": "Clave de modo en vivo/producción",
"authenticatedRequest": "Solicitud Autenticada",
"keepKeysSecret": "¡Mantén tus claves en secreto!",
"keepKeysSecretDescription": "Nunca expongas las claves API en código del lado del cliente, control de versiones o foros públicos.",
"errors": "Errores",
"errorsDescription": "SmoothSchedule usa códigos de respuesta HTTP convencionales para indicar el éxito o fracaso de una solicitud API.",
"httpStatusCodes": "Códigos de Estado HTTP",
"errorResponse": "Respuesta de Error",
"statusOk": "La solicitud fue exitosa.",
"statusCreated": "Se creó un nuevo recurso.",
"statusBadRequest": "Parámetros de solicitud inválidos.",
"statusUnauthorized": "Clave API inválida o faltante.",
"statusForbidden": "La clave API carece de los permisos requeridos.",
"statusNotFound": "El recurso solicitado no existe.",
"statusConflict": "Conflicto de recursos (ej., doble reserva).",
"statusTooManyRequests": "Límite de tasa excedido.",
"statusServerError": "Algo salió mal de nuestro lado.",
"rateLimits": "Límites de Tasa",
"rateLimitsDescription": "La API implementa límites de tasa para asegurar un uso justo y estabilidad.",
"limits": "Límites",
"requestsPerHour": "solicitudes por hora por clave API",
"requestsPerMinute": "solicitudes por minuto límite de ráfaga",
"rateLimitHeaders": "Encabezados de Límite de Tasa",
"rateLimitHeadersDescription": "Cada respuesta incluye encabezados con tu estado actual de límite de tasa.",
"business": "Negocio",
"businessObject": "El objeto Negocio",
"businessObjectDescription": "El objeto Negocio representa la configuración y ajustes de tu negocio.",
"attributes": "Atributos",
"retrieveBusiness": "Obtener negocio",
"retrieveBusinessDescription": "Obtiene el negocio asociado con tu clave API.",
"requiredScope": "Alcance requerido",
"services": "Servicios",
"serviceObject": "El objeto Servicio",
"serviceObjectDescription": "Los servicios representan las ofertas que tu negocio proporciona y que los clientes pueden reservar.",
"listServices": "Listar todos los servicios",
"listServicesDescription": "Devuelve una lista de todos los servicios activos de tu negocio.",
"retrieveService": "Obtener un servicio",
"resources": "Recursos",
"resourceObject": "El objeto Recurso",
"resourceObjectDescription": "Los recursos son las entidades reservables en tu negocio (miembros del personal, salas, equipos).",
"listResources": "Listar todos los recursos",
"retrieveResource": "Obtener un recurso",
"availability": "Disponibilidad",
"checkAvailability": "Verificar disponibilidad",
"checkAvailabilityDescription": "Devuelve los horarios disponibles para un servicio y rango de fechas dado.",
"parameters": "Parámetros",
"appointments": "Citas",
"appointmentObject": "El objeto Cita",
"appointmentObjectDescription": "Las citas representan reservas programadas entre clientes y recursos.",
"createAppointment": "Crear una cita",
"createAppointmentDescription": "Crea una nueva reserva de cita.",
"retrieveAppointment": "Obtener una cita",
"updateAppointment": "Actualizar una cita",
"cancelAppointment": "Cancelar una cita",
"listAppointments": "Listar todas las citas",
"customers": "Clientes",
"customerObject": "El objeto Cliente",
"customerObjectDescription": "Los clientes son las personas que reservan citas con tu negocio.",
"createCustomer": "Crear un cliente",
"retrieveCustomer": "Obtener un cliente",
"updateCustomer": "Actualizar un cliente",
"listCustomers": "Listar todos los clientes",
"webhooks": "Webhooks",
"webhookEvents": "Eventos de webhook",
"webhookEventsDescription": "Los webhooks te permiten recibir notificaciones en tiempo real cuando ocurren eventos en tu negocio.",
"eventTypes": "Tipos de eventos",
"webhookPayload": "Carga de Webhook",
"createWebhook": "Crear un webhook",
"createWebhookDescription": "Crea una nueva suscripción de webhook. La respuesta incluye un secreto que usarás para verificar las firmas de webhook.",
"secretOnlyOnce": "El secreto solo se muestra una vez",
"secretOnlyOnceDescription": ", así que guárdalo de forma segura.",
"listWebhooks": "Listar webhooks",
"deleteWebhook": "Eliminar un webhook",
"verifySignatures": "Verificar firmas",
"verifySignaturesDescription": "Cada solicitud de webhook incluye una firma en el encabezado X-Webhook-Signature. Debes verificar esta firma para asegurar que la solicitud provino de SmoothSchedule.",
"signatureFormat": "Formato de firma",
"signatureFormatDescription": "El encabezado de firma contiene dos valores separados por un punto: una marca de tiempo y la firma HMAC-SHA256.",
"verificationSteps": "Pasos de verificación",
"verificationStep1": "Extrae la marca de tiempo y la firma del encabezado",
"verificationStep2": "Concatena la marca de tiempo, un punto y el cuerpo crudo de la solicitud",
"verificationStep3": "Calcula HMAC-SHA256 usando tu secreto de webhook",
"verificationStep4": "Compara la firma calculada con la firma recibida",
"saveYourSecret": "¡Guarda tu secreto!",
"saveYourSecretDescription": "El secreto del webhook solo se devuelve una vez cuando se crea el webhook. Guárdalo de forma segura para la verificación de firmas.",
"endpoint": "Endpoint",
"request": "Solicitud",
"response": "Respuesta"
}
},
"dashboard": {
"title": "Panel",