Files
smoothschedule/frontend/src/pages/help/HelpApiOverview.tsx
poduck 464726ee3e Update staff roles, documentation, and add tenant API
Staff Roles:
- Remove Support Staff default role (now Manager and Staff only)
- Add position field for custom role ordering
- Update StaffRolesSettings with improved permission UI
- Add RolePermissions component for visual permission display

Documentation Updates:
- HelpStaff: Explain two-tier permission system (User Roles + Staff Roles)
- HelpSettingsStaffRoles: Update default roles, add settings access permissions
- HelpComprehensive: Update staff roles section with correct role structure
- HelpCustomers: Add customer creation and onboarding sections
- HelpContracts: Add lifecycle, snapshotting, and signing experience docs
- HelpSettingsAppearance: Update with 20 color palettes and navigation text

Tenant API:
- Add new isolated API at /tenant-api/v1/ for third-party integrations
- Token-based authentication with scope permissions
- Endpoints: business, services, resources, availability, bookings, customers, webhooks

Tests:
- Add test coverage for Celery tasks across modules
- Reorganize schedule view tests for better maintainability

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-24 20:46:36 -05:00

755 lines
40 KiB
TypeScript

/**
* Help API Overview Page
*
* Tenant-facing API documentation overview.
*/
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate, Link } from 'react-router-dom';
import {
ArrowLeft,
Code,
Key,
Shield,
AlertCircle,
Zap,
Book,
HelpCircle,
ChevronRight,
Calendar,
Users,
Settings,
Webhook,
CheckCircle,
XCircle,
} from 'lucide-react';
const HelpApiOverview: React.FC = () => {
const { t } = useTranslation();
const navigate = useNavigate();
return (
<div className="max-w-4xl mx-auto py-8 px-4">
{/* Back Button */}
<button
onClick={() => navigate(-1)}
className="flex items-center gap-2 text-brand-600 hover:text-brand-700 dark:text-brand-400 mb-6"
>
<ArrowLeft size={20} />
{t('common.back', 'Back')}
</button>
{/* Header */}
<div className="mb-8">
<div className="flex items-center gap-3 mb-4">
<div className="w-12 h-12 rounded-xl bg-brand-100 dark:bg-brand-900/30 flex items-center justify-center">
<Code size={24} className="text-brand-600 dark:text-brand-400" />
</div>
<div>
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
API Overview
</h1>
<p className="text-gray-500 dark:text-gray-400">
REST API for third-party integrations
</p>
</div>
</div>
</div>
{/* Introduction Section */}
<section className="mb-10">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
<Book size={20} className="text-brand-500" />
Introduction
</h2>
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
<p className="text-gray-600 dark:text-gray-300 mb-4">
SmoothSchedule provides a comprehensive REST API that allows you to integrate your scheduling
platform with third-party applications, build custom tools, and automate workflows.
</p>
<div className="bg-gray-50 dark:bg-gray-700/50 rounded-lg p-4 mb-4">
<h4 className="font-medium text-gray-900 dark:text-white mb-2">Base URL</h4>
<code className="text-sm text-brand-600 dark:text-brand-400">
https://your-subdomain.smoothschedule.com/tenant-api/v1/
</code>
</div>
<div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
<div className="flex items-start gap-2">
<Book size={18} className="text-blue-600 dark:text-blue-400 mt-0.5" />
<div>
<p className="text-sm text-blue-800 dark:text-blue-300">
<strong>Tenant Remote API:</strong> This API is designed for third-party integrations.
All requests require Bearer token authentication with appropriate scopes.
</p>
</div>
</div>
</div>
</div>
</section>
{/* Authentication Section */}
<section className="mb-10">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
<Key size={20} className="text-brand-500" />
Authentication
</h2>
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
<p className="text-gray-600 dark:text-gray-300 mb-4">
All API requests require authentication using Bearer tokens in the Authorization header.
</p>
<div className="mb-6">
<h4 className="font-medium text-gray-900 dark:text-white mb-3">Token Format</h4>
<div className="space-y-2">
<div className="flex items-start gap-3 p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
<CheckCircle size={18} className="text-green-500 mt-0.5" />
<div>
<code className="text-sm text-gray-900 dark:text-white font-mono">ss_live_xxxxxxxxx</code>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">Production environment</p>
</div>
</div>
<div className="flex items-start gap-3 p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
<AlertCircle size={18} className="text-yellow-500 mt-0.5" />
<div>
<code className="text-sm text-gray-900 dark:text-white font-mono">ss_test_xxxxxxxxx</code>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">Sandbox environment (safe for testing)</p>
</div>
</div>
</div>
</div>
<div className="mb-6">
<h4 className="font-medium text-gray-900 dark:text-white mb-3">Example Request</h4>
<div className="bg-gray-900 dark:bg-black rounded-lg p-4 overflow-x-auto">
<pre className="text-sm text-gray-100 font-mono">
{`curl -X GET "https://demo.smoothschedule.com/tenant-api/v1/services/" \\
-H "Authorization: Bearer ss_live_xxxxxxxxx" \\
-H "Content-Type: application/json"`}
</pre>
</div>
</div>
<div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4">
<div className="flex items-start gap-2">
<Shield size={18} className="text-yellow-600 dark:text-yellow-400 mt-0.5" />
<div>
<p className="text-sm text-yellow-800 dark:text-yellow-300">
<strong>Security:</strong> API tokens are created in Business Settings API.
Each token has configurable scopes that control access to specific endpoints and operations.
</p>
</div>
</div>
</div>
</div>
</section>
{/* Available Scopes Section */}
<section className="mb-10">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
<Shield size={20} className="text-brand-500" />
Available Scopes
</h2>
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
<p className="text-gray-600 dark:text-gray-300 mb-4">
Control which operations your API token can perform by selecting scopes:
</p>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-gray-200 dark:border-gray-700">
<th className="text-left py-3 px-4 font-medium text-gray-900 dark:text-white">Scope</th>
<th className="text-left py-3 px-4 font-medium text-gray-900 dark:text-white">Description</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
<tr>
<td className="py-3 px-4">
<code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">services:read</code>
</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">View services and pricing</td>
</tr>
<tr>
<td className="py-3 px-4">
<code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">resources:read</code>
</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">View resources and staff</td>
</tr>
<tr>
<td className="py-3 px-4">
<code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">availability:read</code>
</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">Check time slot availability</td>
</tr>
<tr>
<td className="py-3 px-4">
<code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">bookings:read</code>
</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">View appointments</td>
</tr>
<tr>
<td className="py-3 px-4">
<code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">bookings:write</code>
</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">Create, update, cancel appointments</td>
</tr>
<tr>
<td className="py-3 px-4">
<code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">customers:read</code>
</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">View customer information</td>
</tr>
<tr>
<td className="py-3 px-4">
<code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">customers:write</code>
</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">Create and update customers</td>
</tr>
<tr>
<td className="py-3 px-4">
<code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">business:read</code>
</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">View business information</td>
</tr>
<tr>
<td className="py-3 px-4">
<code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">webhooks:manage</code>
</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">Manage webhook subscriptions</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
{/* Filtering & Sorting Section */}
<section className="mb-10">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
<Code size={20} className="text-brand-500" />
Filtering & Sorting
</h2>
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
<p className="text-gray-600 dark:text-gray-300 mb-4">
All list endpoints support filtering and sorting via query parameters.
</p>
{/* Comparison Operators */}
<div className="mb-6">
<h4 className="font-medium text-gray-900 dark:text-white mb-3">Comparison Operators</h4>
<p className="text-sm text-gray-600 dark:text-gray-300 mb-3">
For numeric fields, append these suffixes to filter by comparison:
</p>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-gray-200 dark:border-gray-700">
<th className="text-left py-2 px-3 font-medium text-gray-900 dark:text-white">Suffix</th>
<th className="text-left py-2 px-3 font-medium text-gray-900 dark:text-white">Meaning</th>
<th className="text-left py-2 px-3 font-medium text-gray-900 dark:text-white">Example</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">(none)</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Equals</td>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">price=5000</code></td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">__lt</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Less than</td>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">price__lt=5000</code></td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">__lte</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Less than or equal</td>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">price__lte=5000</code></td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">__gt</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Greater than</td>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">price__gt=5000</code></td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">__gte</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Greater than or equal</td>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">price__gte=5000</code></td>
</tr>
</tbody>
</table>
</div>
</div>
{/* Sorting */}
<div className="mb-6">
<h4 className="font-medium text-gray-900 dark:text-white mb-3">Sorting</h4>
<p className="text-sm text-gray-600 dark:text-gray-300 mb-3">
Use the <code className="bg-gray-100 dark:bg-gray-700 px-1 rounded text-xs">ordering</code> parameter to sort results.
Prefix with <code className="bg-gray-100 dark:bg-gray-700 px-1 rounded text-xs">-</code> for descending order.
</p>
<div className="bg-gray-900 dark:bg-black rounded-lg p-4 overflow-x-auto">
<pre className="text-sm text-gray-100 font-mono">
{`# Ascending (oldest first)
GET /tenant-api/v1/bookings/?ordering=start_time
# Descending (newest first)
GET /tenant-api/v1/bookings/?ordering=-start_time`}
</pre>
</div>
</div>
{/* Services Filters */}
<div className="mb-6">
<h4 className="font-medium text-gray-900 dark:text-white mb-3">Services Endpoint Filters</h4>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-gray-200 dark:border-gray-700">
<th className="text-left py-2 px-3 font-medium text-gray-900 dark:text-white">Parameter</th>
<th className="text-left py-2 px-3 font-medium text-gray-900 dark:text-white">Type</th>
<th className="text-left py-2 px-3 font-medium text-gray-900 dark:text-white">Description</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">search</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">string</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Search by name (partial match)</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">name</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">string</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Exact name match</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">price</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">integer</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Price in cents (supports __lt, __lte, __gt, __gte)</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">duration</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">integer</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Duration in minutes (supports __lt, __lte, __gt, __gte)</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">variable_pricing</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">boolean</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Filter by variable pricing (true/false)</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">requires_manual_scheduling</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">boolean</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Filter by manual scheduling requirement</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">ordering</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">string</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">name, price, duration, display_order (prefix - for desc)</td>
</tr>
</tbody>
</table>
</div>
</div>
{/* Bookings Filters */}
<div className="mb-6">
<h4 className="font-medium text-gray-900 dark:text-white mb-3">Bookings Endpoint Filters</h4>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-gray-200 dark:border-gray-700">
<th className="text-left py-2 px-3 font-medium text-gray-900 dark:text-white">Parameter</th>
<th className="text-left py-2 px-3 font-medium text-gray-900 dark:text-white">Type</th>
<th className="text-left py-2 px-3 font-medium text-gray-900 dark:text-white">Description</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">start_date</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">date</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Filter from date (YYYY-MM-DD)</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">end_date</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">date</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Filter to date (YYYY-MM-DD)</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">start_datetime</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">datetime</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Filter from datetime (ISO 8601)</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">end_datetime</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">datetime</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Filter to datetime (ISO 8601)</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">status</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">string</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">SCHEDULED, CONFIRMED, COMPLETED, CANCELLED</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">service_id</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">integer</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Filter by service</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">customer_id</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">integer</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Filter by customer</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">resource_id</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">integer</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">Filter by resource</td>
</tr>
<tr>
<td className="py-2 px-3"><code className="text-xs bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">ordering</code></td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">string</td>
<td className="py-2 px-3 text-gray-600 dark:text-gray-300">start_time, end_time, created_at, updated_at</td>
</tr>
</tbody>
</table>
</div>
</div>
{/* Example */}
<div>
<h4 className="font-medium text-gray-900 dark:text-white mb-3">Examples</h4>
<div className="bg-gray-900 dark:bg-black rounded-lg p-4 overflow-x-auto">
<pre className="text-sm text-gray-100 font-mono">
{`# Services under $50 with duration over 30 minutes
GET /tenant-api/v1/services/?price__lt=5000&duration__gt=30&ordering=price
# Bookings between two dates, newest first
GET /tenant-api/v1/bookings/?start_date=2025-01-01&end_date=2025-01-31&ordering=-start_time
# Customers search with sorting by last name
GET /tenant-api/v1/customers/?search=john&ordering=last_name`}
</pre>
</div>
</div>
</div>
</section>
{/* Rate Limiting Section */}
<section className="mb-10">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
<Zap size={20} className="text-brand-500" />
Rate Limiting
</h2>
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
<p className="text-gray-600 dark:text-gray-300 mb-4">
API requests are rate-limited to ensure fair usage and platform stability:
</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div className="flex items-start gap-3 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
<Zap size={20} className="text-blue-500 mt-0.5" />
<div>
<h4 className="font-medium text-gray-900 dark:text-white">Global Limit</h4>
<p className="text-sm text-gray-500 dark:text-gray-400">1,000 requests per hour</p>
</div>
</div>
<div className="flex items-start gap-3 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
<Zap size={20} className="text-orange-500 mt-0.5" />
<div>
<h4 className="font-medium text-gray-900 dark:text-white">Burst Limit</h4>
<p className="text-sm text-gray-500 dark:text-gray-400">100 requests per minute</p>
</div>
</div>
</div>
<div className="bg-gray-50 dark:bg-gray-700/50 rounded-lg p-4">
<h4 className="font-medium text-gray-900 dark:text-white mb-2">Response Headers</h4>
<div className="space-y-1 text-sm font-mono text-gray-600 dark:text-gray-300">
<div><code>X-RateLimit-Limit</code> - Maximum requests allowed</div>
<div><code>X-RateLimit-Remaining</code> - Requests remaining in window</div>
<div><code>X-RateLimit-Reset</code> - Unix timestamp when limit resets</div>
</div>
</div>
</div>
</section>
{/* Error Responses Section */}
<section className="mb-10">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
<AlertCircle size={20} className="text-brand-500" />
Error Responses
</h2>
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
<p className="text-gray-600 dark:text-gray-300 mb-4">
All errors follow a consistent JSON format:
</p>
<div className="bg-gray-900 dark:bg-black rounded-lg p-4 overflow-x-auto mb-4">
<pre className="text-sm text-gray-100 font-mono">
{`{
"error": "validation_error",
"message": "Invalid request data",
"details": {
"start_time": ["This field is required."],
"service_id": ["Invalid service ID."]
}
}`}
</pre>
</div>
<h4 className="font-medium text-gray-900 dark:text-white mb-3">HTTP Status Codes</h4>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-gray-200 dark:border-gray-700">
<th className="text-left py-3 px-4 font-medium text-gray-900 dark:text-white">Code</th>
<th className="text-left py-3 px-4 font-medium text-gray-900 dark:text-white">Status</th>
<th className="text-left py-3 px-4 font-medium text-gray-900 dark:text-white">Description</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
<tr>
<td className="py-3 px-4"><code className="text-green-600 dark:text-green-400">200</code></td>
<td className="py-3 px-4 text-gray-900 dark:text-white">OK</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">Request succeeded</td>
</tr>
<tr>
<td className="py-3 px-4"><code className="text-green-600 dark:text-green-400">201</code></td>
<td className="py-3 px-4 text-gray-900 dark:text-white">Created</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">Resource created successfully</td>
</tr>
<tr>
<td className="py-3 px-4"><code className="text-red-600 dark:text-red-400">400</code></td>
<td className="py-3 px-4 text-gray-900 dark:text-white">Bad Request</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">Invalid request data</td>
</tr>
<tr>
<td className="py-3 px-4"><code className="text-red-600 dark:text-red-400">401</code></td>
<td className="py-3 px-4 text-gray-900 dark:text-white">Unauthorized</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">Missing or invalid token</td>
</tr>
<tr>
<td className="py-3 px-4"><code className="text-red-600 dark:text-red-400">403</code></td>
<td className="py-3 px-4 text-gray-900 dark:text-white">Forbidden</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">Insufficient scope permissions</td>
</tr>
<tr>
<td className="py-3 px-4"><code className="text-red-600 dark:text-red-400">404</code></td>
<td className="py-3 px-4 text-gray-900 dark:text-white">Not Found</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">Resource does not exist</td>
</tr>
<tr>
<td className="py-3 px-4"><code className="text-red-600 dark:text-red-400">409</code></td>
<td className="py-3 px-4 text-gray-900 dark:text-white">Conflict</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">Resource conflict (e.g., double booking)</td>
</tr>
<tr>
<td className="py-3 px-4"><code className="text-red-600 dark:text-red-400">429</code></td>
<td className="py-3 px-4 text-gray-900 dark:text-white">Too Many Requests</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">Rate limit exceeded</td>
</tr>
<tr>
<td className="py-3 px-4"><code className="text-red-600 dark:text-red-400">500</code></td>
<td className="py-3 px-4 text-gray-900 dark:text-white">Internal Server Error</td>
<td className="py-3 px-4 text-gray-600 dark:text-gray-300">Server error (contact support)</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
{/* Sandbox Mode Section */}
<section className="mb-10">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
<Settings size={20} className="text-brand-500" />
Sandbox Mode
</h2>
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
<p className="text-gray-600 dark:text-gray-300 mb-4">
Test your integration safely without affecting production data:
</p>
<ul className="space-y-3 mb-4">
<li className="flex items-start gap-3">
<CheckCircle size={18} className="text-green-500 mt-0.5" />
<div>
<p className="text-gray-900 dark:text-white font-medium">Test Tokens</p>
<p className="text-sm text-gray-600 dark:text-gray-300">
Tokens prefixed with <code className="bg-gray-100 dark:bg-gray-700 px-1 rounded text-xs">ss_test_</code> work with sandbox data only
</p>
</div>
</li>
<li className="flex items-start gap-3">
<CheckCircle size={18} className="text-green-500 mt-0.5" />
<div>
<p className="text-gray-900 dark:text-white font-medium">Safe Testing</p>
<p className="text-sm text-gray-600 dark:text-gray-300">
Create, update, and delete test appointments without affecting real bookings
</p>
</div>
</li>
<li className="flex items-start gap-3">
<CheckCircle size={18} className="text-green-500 mt-0.5" />
<div>
<p className="text-gray-900 dark:text-white font-medium">Easy Toggle</p>
<p className="text-sm text-gray-600 dark:text-gray-300">
Switch between test and live modes in Business Settings API
</p>
</div>
</li>
</ul>
<div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4">
<div className="flex items-start gap-2">
<AlertCircle size={18} className="text-yellow-600 dark:text-yellow-400 mt-0.5" />
<div>
<p className="text-sm text-yellow-800 dark:text-yellow-300">
<strong>Tip:</strong> Always test with sandbox tokens before using production tokens in your application.
</p>
</div>
</div>
</div>
</div>
</section>
{/* Quick Links Section */}
<section className="mb-10">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
<Book size={20} className="text-brand-500" />
API Endpoints
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Link
to="/dashboard/help/api/appointments"
className="group bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 hover:border-brand-500 dark:hover:border-brand-500 transition-colors"
>
<div className="flex items-start gap-3 mb-3">
<div className="w-10 h-10 rounded-lg bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center flex-shrink-0">
<Calendar size={20} className="text-blue-600 dark:text-blue-400" />
</div>
<div>
<h4 className="font-semibold text-gray-900 dark:text-white group-hover:text-brand-600 dark:group-hover:text-brand-400 mb-1">
Appointments API
</h4>
<p className="text-sm text-gray-500 dark:text-gray-400">
Create, manage, and query appointments
</p>
</div>
</div>
<div className="flex items-center justify-end text-brand-600 dark:text-brand-400 text-sm font-medium">
View Docs
<ChevronRight size={16} className="ml-1" />
</div>
</Link>
<Link
to="/dashboard/help/api/services"
className="group bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 hover:border-brand-500 dark:hover:border-brand-500 transition-colors"
>
<div className="flex items-start gap-3 mb-3">
<div className="w-10 h-10 rounded-lg bg-green-100 dark:bg-green-900/30 flex items-center justify-center flex-shrink-0">
<Settings size={20} className="text-green-600 dark:text-green-400" />
</div>
<div>
<h4 className="font-semibold text-gray-900 dark:text-white group-hover:text-brand-600 dark:group-hover:text-brand-400 mb-1">
Services API
</h4>
<p className="text-sm text-gray-500 dark:text-gray-400">
Access service catalog and pricing
</p>
</div>
</div>
<div className="flex items-center justify-end text-brand-600 dark:text-brand-400 text-sm font-medium">
View Docs
<ChevronRight size={16} className="ml-1" />
</div>
</Link>
<Link
to="/dashboard/help/api/resources"
className="group bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 hover:border-brand-500 dark:hover:border-brand-500 transition-colors"
>
<div className="flex items-start gap-3 mb-3">
<div className="w-10 h-10 rounded-lg bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center flex-shrink-0">
<Users size={20} className="text-purple-600 dark:text-purple-400" />
</div>
<div>
<h4 className="font-semibold text-gray-900 dark:text-white group-hover:text-brand-600 dark:group-hover:text-brand-400 mb-1">
Resources API
</h4>
<p className="text-sm text-gray-500 dark:text-gray-400">
Staff, rooms, and equipment data
</p>
</div>
</div>
<div className="flex items-center justify-end text-brand-600 dark:text-brand-400 text-sm font-medium">
View Docs
<ChevronRight size={16} className="ml-1" />
</div>
</Link>
<Link
to="/dashboard/help/api/customers"
className="group bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 hover:border-brand-500 dark:hover:border-brand-500 transition-colors"
>
<div className="flex items-start gap-3 mb-3">
<div className="w-10 h-10 rounded-lg bg-orange-100 dark:bg-orange-900/30 flex items-center justify-center flex-shrink-0">
<Users size={20} className="text-orange-600 dark:text-orange-400" />
</div>
<div>
<h4 className="font-semibold text-gray-900 dark:text-white group-hover:text-brand-600 dark:group-hover:text-brand-400 mb-1">
Customers API
</h4>
<p className="text-sm text-gray-500 dark:text-gray-400">
Customer profiles and contact info
</p>
</div>
</div>
<div className="flex items-center justify-end text-brand-600 dark:text-brand-400 text-sm font-medium">
View Docs
<ChevronRight size={16} className="ml-1" />
</div>
</Link>
<Link
to="/dashboard/help/api/webhooks"
className="group bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 hover:border-brand-500 dark:hover:border-brand-500 transition-colors"
>
<div className="flex items-start gap-3 mb-3">
<div className="w-10 h-10 rounded-lg bg-pink-100 dark:bg-pink-900/30 flex items-center justify-center flex-shrink-0">
<Webhook size={20} className="text-pink-600 dark:text-pink-400" />
</div>
<div>
<h4 className="font-semibold text-gray-900 dark:text-white group-hover:text-brand-600 dark:group-hover:text-brand-400 mb-1">
Webhooks
</h4>
<p className="text-sm text-gray-500 dark:text-gray-400">
Real-time event notifications
</p>
</div>
</div>
<div className="flex items-center justify-end text-brand-600 dark:text-brand-400 text-sm font-medium">
View Docs
<ChevronRight size={16} className="ml-1" />
</div>
</Link>
</div>
</section>
{/* Need More Help */}
<section className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6 text-center">
<HelpCircle size={32} className="mx-auto text-brand-500 mb-3" />
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
Need More Help?
</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Our support team is ready to help with API integration questions.
</p>
<button
onClick={() => navigate('/dashboard/tickets')}
className="px-6 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 transition-colors"
>
Contact Support
</button>
</section>
</div>
);
};
export default HelpApiOverview;