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>
This commit is contained in:
@@ -71,7 +71,7 @@ const HelpApiOverview: React.FC = () => {
|
||||
<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/api/v1/
|
||||
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">
|
||||
@@ -79,15 +79,8 @@ const HelpApiOverview: React.FC = () => {
|
||||
<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>Interactive Documentation:</strong> Explore and test API endpoints at{' '}
|
||||
<a
|
||||
href="/api/v1/docs/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="bg-blue-100 dark:bg-blue-900/40 px-1 rounded font-mono hover:underline"
|
||||
>
|
||||
/api/v1/docs/
|
||||
</a>
|
||||
<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>
|
||||
@@ -130,7 +123,7 @@ const HelpApiOverview: React.FC = () => {
|
||||
<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/api/v1/services/" \\
|
||||
{`curl -X GET "https://demo.smoothschedule.com/tenant-api/v1/services/" \\
|
||||
-H "Authorization: Bearer ss_live_xxxxxxxxx" \\
|
||||
-H "Content-Type: application/json"`}
|
||||
</pre>
|
||||
@@ -230,6 +223,216 @@ const HelpApiOverview: React.FC = () => {
|
||||
</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">
|
||||
|
||||
Reference in New Issue
Block a user