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>
755 lines
40 KiB
TypeScript
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;
|