Add Site Builder help docs and fix FloatingHelpButton paths

- Add HelpSiteBuilder.tsx with comprehensive documentation for the
  drag-and-drop page editor (components, publishing, settings)
- Fix FloatingHelpButton to use /dashboard/help/* paths on tenant sites
- Update HelpComprehensive and HelpAutomations to rename plugins to automations
- Add site-crawler utility with cross-subdomain redirect detection

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-16 22:42:46 -05:00
parent 725a3c5d84
commit 94e37a2522
8 changed files with 913 additions and 189 deletions

View File

@@ -10,76 +10,85 @@ import { Link, useLocation } from 'react-router-dom';
import { HelpCircle } from 'lucide-react';
import { useTranslation } from 'react-i18next';
// Map routes to their help paths
const routeToHelpPath: Record<string, string> = {
'/': '/help/dashboard',
'/dashboard': '/help/dashboard',
'/scheduler': '/help/scheduler',
'/tasks': '/help/tasks',
'/customers': '/help/customers',
'/services': '/help/services',
'/resources': '/help/resources',
'/staff': '/help/staff',
'/time-blocks': '/help/time-blocks',
'/my-availability': '/help/time-blocks',
'/messages': '/help/messages',
'/tickets': '/help/ticketing',
'/payments': '/help/payments',
'/contracts': '/help/contracts',
'/contracts/templates': '/help/contracts',
'/automations': '/help/automations',
'/automations/marketplace': '/help/automations',
'/automations/my-automations': '/help/automations',
'/automations/create': '/help/automations/docs',
'/settings': '/help/settings/general',
'/settings/general': '/help/settings/general',
'/settings/resource-types': '/help/settings/resource-types',
'/settings/booking': '/help/settings/booking',
'/settings/appearance': '/help/settings/appearance',
'/settings/email': '/help/settings/email',
'/settings/domains': '/help/settings/domains',
'/settings/api': '/help/settings/api',
'/settings/auth': '/help/settings/auth',
'/settings/billing': '/help/settings/billing',
'/settings/quota': '/help/settings/quota',
// Platform routes
'/platform/dashboard': '/help/dashboard',
'/platform/businesses': '/help/dashboard',
'/platform/users': '/help/staff',
'/platform/tickets': '/help/ticketing',
// Map route suffixes to their help page suffixes
// These get prefixed appropriately based on context (tenant dashboard or public)
const routeToHelpSuffix: Record<string, string> = {
'/': 'dashboard',
'/dashboard': 'dashboard',
'/scheduler': 'scheduler',
'/tasks': 'tasks',
'/customers': 'customers',
'/services': 'services',
'/resources': 'resources',
'/staff': 'staff',
'/time-blocks': 'time-blocks',
'/my-availability': 'time-blocks',
'/messages': 'messages',
'/tickets': 'ticketing',
'/payments': 'payments',
'/contracts': 'contracts',
'/contracts/templates': 'contracts',
'/automations': 'automations',
'/automations/marketplace': 'automations',
'/automations/my-automations': 'automations',
'/automations/create': 'automations/docs',
'/site-editor': 'site-builder',
'/gallery': 'site-builder',
'/settings': 'settings/general',
'/settings/general': 'settings/general',
'/settings/resource-types': 'settings/resource-types',
'/settings/booking': 'settings/booking',
'/settings/appearance': 'settings/appearance',
'/settings/email': 'settings/email',
'/settings/domains': 'settings/domains',
'/settings/api': 'settings/api',
'/settings/auth': 'settings/auth',
'/settings/billing': 'settings/billing',
'/settings/quota': 'settings/quota',
};
const FloatingHelpButton: React.FC = () => {
const { t } = useTranslation();
const location = useLocation();
// Check if we're on a tenant dashboard route
const isOnDashboard = location.pathname.startsWith('/dashboard');
// Get the help path for the current route
const getHelpPath = (): string => {
// Determine the base help path based on context
const helpBase = isOnDashboard ? '/dashboard/help' : '/help';
// Get the route to look up (strip /dashboard prefix if present)
const lookupPath = isOnDashboard
? location.pathname.replace(/^\/dashboard/, '') || '/'
: location.pathname;
// Exact match first
if (routeToHelpPath[location.pathname]) {
return routeToHelpPath[location.pathname];
if (routeToHelpSuffix[lookupPath]) {
return `${helpBase}/${routeToHelpSuffix[lookupPath]}`;
}
// Try matching with a prefix (for dynamic routes like /customers/:id)
const pathSegments = location.pathname.split('/').filter(Boolean);
const pathSegments = lookupPath.split('/').filter(Boolean);
if (pathSegments.length > 0) {
// Try progressively shorter paths
for (let i = pathSegments.length; i > 0; i--) {
const testPath = '/' + pathSegments.slice(0, i).join('/');
if (routeToHelpPath[testPath]) {
return routeToHelpPath[testPath];
if (routeToHelpSuffix[testPath]) {
return `${helpBase}/${routeToHelpSuffix[testPath]}`;
}
}
}
// Default to the main help guide
return '/help';
// Default to the main help page
return helpBase;
};
const helpPath = getHelpPath();
// Don't show on help pages themselves
if (location.pathname.startsWith('/help')) {
if (location.pathname.includes('/help')) {
return null;
}

View File

@@ -19,68 +19,127 @@ describe('FloatingHelpButton', () => {
);
};
it('renders help link on dashboard', () => {
renderWithRouter('/dashboard');
const link = screen.getByRole('link');
expect(link).toBeInTheDocument();
describe('tenant dashboard routes (prefixed with /dashboard)', () => {
it('renders help link on tenant dashboard', () => {
renderWithRouter('/dashboard');
const link = screen.getByRole('link');
expect(link).toBeInTheDocument();
});
it('links to /dashboard/help/dashboard for /dashboard', () => {
renderWithRouter('/dashboard');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/dashboard/help/dashboard');
});
it('links to /dashboard/help/scheduler for /dashboard/scheduler', () => {
renderWithRouter('/dashboard/scheduler');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/dashboard/help/scheduler');
});
it('links to /dashboard/help/services for /dashboard/services', () => {
renderWithRouter('/dashboard/services');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/dashboard/help/services');
});
it('links to /dashboard/help/resources for /dashboard/resources', () => {
renderWithRouter('/dashboard/resources');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/dashboard/help/resources');
});
it('links to /dashboard/help/settings/general for /dashboard/settings/general', () => {
renderWithRouter('/dashboard/settings/general');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/dashboard/help/settings/general');
});
it('links to /dashboard/help/customers for /dashboard/customers/123', () => {
renderWithRouter('/dashboard/customers/123');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/dashboard/help/customers');
});
it('returns null on /dashboard/help pages', () => {
const { container } = renderWithRouter('/dashboard/help/dashboard');
expect(container.firstChild).toBeNull();
});
it('links to /dashboard/help for unknown dashboard routes', () => {
renderWithRouter('/dashboard/unknown-route');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/dashboard/help');
});
it('links to /dashboard/help/site-builder for /dashboard/site-editor', () => {
renderWithRouter('/dashboard/site-editor');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/dashboard/help/site-builder');
});
it('links to /dashboard/help/site-builder for /dashboard/gallery', () => {
renderWithRouter('/dashboard/gallery');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/dashboard/help/site-builder');
});
});
it('links to correct help page for dashboard', () => {
renderWithRouter('/dashboard');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/help/dashboard');
describe('non-dashboard routes (public/platform)', () => {
it('links to /help/scheduler for /scheduler', () => {
renderWithRouter('/scheduler');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/help/scheduler');
});
it('links to /help/services for /services', () => {
renderWithRouter('/services');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/help/services');
});
it('links to /help/resources for /resources', () => {
renderWithRouter('/resources');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/help/resources');
});
it('links to /help/settings/general for /settings/general', () => {
renderWithRouter('/settings/general');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/help/settings/general');
});
it('returns null on /help pages', () => {
const { container } = renderWithRouter('/help/dashboard');
expect(container.firstChild).toBeNull();
});
it('links to /help for unknown routes', () => {
renderWithRouter('/unknown-route');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/help');
});
it('handles dynamic routes by matching prefix', () => {
renderWithRouter('/customers/123');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/help/customers');
});
});
it('links to correct help page for scheduler', () => {
renderWithRouter('/scheduler');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/help/scheduler');
});
describe('accessibility', () => {
it('has aria-label', () => {
renderWithRouter('/dashboard');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('aria-label', 'Help');
});
it('links to correct help page for services', () => {
renderWithRouter('/services');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/help/services');
});
it('links to correct help page for resources', () => {
renderWithRouter('/resources');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/help/resources');
});
it('links to correct help page for settings', () => {
renderWithRouter('/settings/general');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/help/settings/general');
});
it('returns null on help pages', () => {
const { container } = renderWithRouter('/help/dashboard');
expect(container.firstChild).toBeNull();
});
it('has aria-label', () => {
renderWithRouter('/dashboard');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('aria-label', 'Help');
});
it('has title attribute', () => {
renderWithRouter('/dashboard');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('title', 'Help');
});
it('links to default help for unknown routes', () => {
renderWithRouter('/unknown-route');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/help');
});
it('handles dynamic routes by matching prefix', () => {
renderWithRouter('/customers/123');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/help/customers');
it('has title attribute', () => {
renderWithRouter('/dashboard');
const link = screen.getByRole('link');
expect(link).toHaveAttribute('title', 'Help');
});
});
});