feat(frontend): Align documentation with development - complete frontend audit
Some checks are pending
CI / Backend CI (push) Waiting to run
CI / Frontend CI (push) Waiting to run
CI / Security Scan (push) Waiting to run
CI / CI Summary (push) Blocked by required conditions

- Add Sales/Commissions routes to router (11 new routes)
- Complete authStore (refreshTokens, updateProfile methods)
- Add JSDoc documentation to 40+ components across all categories
- Create FRONTEND-ROUTING.md with complete route mapping
- Create FRONTEND-PAGES-SPEC.md with 38 page specifications
- Update FRONTEND_INVENTORY.yml to v4.1.0 with resolved gaps

Components documented: ai/, audit/, commissions/, feature-flags/,
notifications/, sales/, storage/, webhooks/, whatsapp/

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Adrian Flores Cortes 2026-01-25 01:23:50 -06:00
parent 0b90d87c1f
commit f853c49568
45 changed files with 2511 additions and 11 deletions

View File

@ -17,6 +17,32 @@ interface AIChatProps {
onUsageUpdate?: () => void;
}
/**
* AI Chat component that provides an interactive chat interface with an AI assistant.
* Supports conversation history, model selection, and configurable system prompts.
*
* @description Interactive chat component for communicating with AI models via OpenRouter.
* Features auto-scrolling, message history, loading states, and customizable appearance.
*
* @param {string} [systemPrompt] - Custom system prompt to override the default AI behavior
* @param {string} [placeholder='Type your message...'] - Placeholder text for the input field
* @param {string} [className] - Additional CSS classes to apply to the container
* @param {boolean} [showModelSelector=false] - Whether to show the AI model selection dropdown
* @param {() => void} [onUsageUpdate] - Callback function triggered after each AI response for usage tracking
*
* @example
* // Basic usage
* <AIChat />
*
* @example
* // With custom system prompt and model selector
* <AIChat
* systemPrompt="You are a helpful coding assistant."
* showModelSelector={true}
* onUsageUpdate={() => refetchUsage()}
* className="h-[600px]"
* />
*/
export function AIChat({
systemPrompt,
placeholder = 'Type your message...',

View File

@ -13,6 +13,25 @@ interface AIConfigForm {
log_conversations: boolean;
}
/**
* AI Settings component for configuring AI integration at the organization level.
* Provides controls for enabling/disabling AI, selecting models, adjusting parameters,
* and viewing usage statistics.
*
* @description Administrative panel for managing AI configuration including model selection,
* temperature, max tokens, system prompts, and privacy settings. Displays real-time health
* status and usage metrics.
*
* @example
* // Basic usage in settings page
* <AISettings />
*
* @example
* // Typically used within an admin or settings layout
* <SettingsLayout>
* <AISettings />
* </SettingsLayout>
*/
export function AISettings() {
const { data: config, isLoading: configLoading } = useAIConfig();
const { data: models } = useAIModels();

View File

@ -8,6 +8,42 @@ export interface ChatMessageProps {
isLoading?: boolean;
}
/**
* Chat message component that renders a single message in the AI chat interface.
* Supports different visual styles for user, assistant, and system messages.
*
* @description Displays a chat message with role-specific styling, avatar icons,
* and optional timestamp. Handles loading states with animated dots for pending
* assistant responses. System messages are rendered as centered badges.
*
* @param {'user' | 'assistant' | 'system'} role - The role of the message sender
* @param {string} content - The text content of the message
* @param {Date} [timestamp] - Optional timestamp to display below the message
* @param {boolean} [isLoading=false] - Whether to show loading animation instead of content
*
* @example
* // User message
* <ChatMessage
* role="user"
* content="Hello, can you help me?"
* timestamp={new Date()}
* />
*
* @example
* // Assistant message with loading state
* <ChatMessage
* role="assistant"
* content=""
* isLoading={true}
* />
*
* @example
* // System message
* <ChatMessage
* role="system"
* content="Conversation started"
* />
*/
export function ChatMessage({ role, content, timestamp, isLoading }: ChatMessageProps) {
const isUser = role === 'user';
const isSystem = role === 'system';

View File

@ -15,8 +15,13 @@ import {
} from 'lucide-react';
import clsx from 'clsx';
/**
* Props for the ActivityTimeline component.
*/
interface ActivityTimelineProps {
/** Array of activity logs to display in the timeline */
activities: ActivityLog[];
/** Whether the component is in a loading state */
isLoading?: boolean;
}
@ -46,6 +51,26 @@ const colorMap: Record<string, string> = {
payment: 'bg-emerald-100 text-emerald-600 dark:bg-emerald-900/30 dark:text-emerald-400',
};
/**
* Displays a vertical timeline of user activity events.
*
* @description Renders a chronological list of activity logs with type-specific icons
* and color coding. Each activity shows the type label, description, user name,
* resource type, relative timestamp, and any associated metadata. Supports loading
* state with animated skeleton placeholders.
*
* @param props - The component props
* @param props.activities - Array of activity logs to display in the timeline
* @param props.isLoading - Whether to show loading skeleton instead of data
*
* @example
* ```tsx
* <ActivityTimeline
* activities={recentActivities}
* isLoading={isLoadingActivities}
* />
* ```
*/
export function ActivityTimeline({ activities, isLoading }: ActivityTimelineProps) {
if (isLoading) {
return (

View File

@ -4,9 +4,15 @@ import { getAuditActionLabel } from '@/hooks/useAudit';
import { Filter, X, Search } from 'lucide-react';
import clsx from 'clsx';
/**
* Props for the AuditFilters component.
*/
interface AuditFiltersProps {
/** Current filter parameters applied to the audit log query */
filters: QueryAuditLogsParams;
/** Callback invoked when any filter value changes */
onFiltersChange: (filters: QueryAuditLogsParams) => void;
/** List of available entity types for the entity type filter dropdown */
entityTypes?: string[];
}
@ -21,6 +27,28 @@ const AUDIT_ACTIONS: AuditAction[] = [
'import',
];
/**
* Provides a collapsible filter panel for audit log queries.
*
* @description Renders a filter interface with controls for action type, entity type,
* and date range (from/to). Supports expanding/collapsing the filter panel, displays
* active filter count as a badge, and shows removable filter badges when collapsed.
* Automatically resets pagination to page 1 when filters change.
*
* @param props - The component props
* @param props.filters - Current filter parameters applied to the audit log query
* @param props.onFiltersChange - Callback invoked when any filter value changes
* @param props.entityTypes - List of available entity types for the dropdown
*
* @example
* ```tsx
* <AuditFilters
* filters={queryParams}
* onFiltersChange={setQueryParams}
* entityTypes={['user', 'tenant', 'subscription']}
* />
* ```
*/
export function AuditFilters({ filters, onFiltersChange, entityTypes = [] }: AuditFiltersProps) {
const [isOpen, setIsOpen] = useState(false);
@ -181,6 +209,16 @@ export function AuditFilters({ filters, onFiltersChange, entityTypes = [] }: Aud
);
}
/**
* Displays a removable badge for an active filter.
*
* @description Internal component that renders a pill-shaped badge showing
* the filter label with a close button to remove the filter.
*
* @param props - The component props
* @param props.label - Text to display in the badge
* @param props.onRemove - Callback invoked when the remove button is clicked
*/
function FilterBadge({ label, onRemove }: { label: string; onRemove: () => void }) {
return (
<span className="inline-flex items-center gap-1 px-2 py-1 bg-primary-100 dark:bg-primary-900/30 text-primary-700 dark:text-primary-400 rounded-full text-sm">

View File

@ -4,11 +4,35 @@ import { getAuditActionLabel, getAuditActionColor } from '@/hooks/useAudit';
import { ChevronDown, ChevronRight, User, Clock, Globe, Code } from 'lucide-react';
import clsx from 'clsx';
/**
* Props for the AuditLogRow component.
*/
interface AuditLogRowProps {
/** The audit log entry to display */
log: AuditLog;
/** Optional callback invoked when the user clicks to view full details */
onSelect?: (log: AuditLog) => void;
}
/**
* Displays a single audit log entry as an expandable row.
*
* @description Renders an audit log with summary information (action, entity, user, timestamp)
* in a collapsed state. When expanded, shows detailed information including old/new values,
* changed fields, metadata, IP address, endpoint, and response status.
*
* @param props - The component props
* @param props.log - The audit log entry to display
* @param props.onSelect - Optional callback invoked when the user clicks to view full details
*
* @example
* ```tsx
* <AuditLogRow
* log={auditLogEntry}
* onSelect={(log) => openDetailModal(log)}
* />
* ```
*/
export function AuditLogRow({ log, onSelect }: AuditLogRowProps) {
const [expanded, setExpanded] = useState(false);

View File

@ -3,11 +3,35 @@ import { getAuditActionLabel, getAuditActionColor } from '@/hooks/useAudit';
import { Activity, TrendingUp, Users, FileText } from 'lucide-react';
import clsx from 'clsx';
/**
* Props for the AuditStatsCard component.
*/
interface AuditStatsCardProps {
/** Aggregated audit statistics including totals, breakdowns by action/entity, and daily trends */
stats: AuditStats;
/** Whether the component is in a loading state */
isLoading?: boolean;
}
/**
* Displays an overview card with audit statistics and trends.
*
* @description Renders a dashboard card showing key audit metrics including total logs,
* number of unique actions, entity types, and active users. Also displays breakdowns
* by action type and entity type, plus a daily activity trend chart for the last 7 days.
*
* @param props - The component props
* @param props.stats - Aggregated audit statistics object
* @param props.isLoading - Whether to show loading skeleton instead of data
*
* @example
* ```tsx
* <AuditStatsCard
* stats={auditStats}
* isLoading={isLoadingStats}
* />
* ```
*/
export function AuditStatsCard({ stats, isLoading }: AuditStatsCardProps) {
if (isLoading) {
return (

View File

@ -1,12 +1,51 @@
import { DollarSign, Users, Calendar, TrendingUp, Clock, CheckCircle } from 'lucide-react';
import { DashboardSummary, TopEarner, PeriodEarnings } from '../../services/commissions/dashboard.api';
/**
* Props for the CommissionsDashboard component.
*/
interface CommissionsDashboardProps {
/** Summary data containing totals for pending, approved, and paid commissions */
summary: DashboardSummary;
/** List of top earning users with their rankings and total earned amounts */
topEarners: TopEarner[];
/** List of recent commission periods with their earnings data */
periodEarnings: PeriodEarnings[];
}
/**
* Displays a comprehensive dashboard for commission management.
* Shows summary cards with pending, approved, paid commissions and active schemes,
* along with top earners ranking and recent period performance.
*
* @param props - The component props
* @param props.summary - Aggregated commission statistics including totals and counts
* @param props.topEarners - Ranked list of users by earnings
* @param props.periodEarnings - Historical period data with amounts and statuses
*
* @example
* ```tsx
* <CommissionsDashboard
* summary={{
* total_pending: 5000,
* pending_count: 10,
* total_approved: 15000,
* approved_count: 25,
* total_paid: 50000,
* paid_count: 100,
* active_schemes: 5,
* active_assignments: 20,
* currency: 'USD'
* }}
* topEarners={[
* { user_id: '1', user_name: 'John Doe', total_earned: 10000, entries_count: 50, rank: 1 }
* ]}
* periodEarnings={[
* { period_id: '1', period_name: 'January 2026', starts_at: '2026-01-01', ends_at: '2026-01-31', total_amount: 25000, status: 'paid' }
* ]}
* />
* ```
*/
export function CommissionsDashboard({ summary, topEarners, periodEarnings }: CommissionsDashboardProps) {
const formatCurrency = (amount: number) =>
new Intl.NumberFormat('en-US', { style: 'currency', currency: summary.currency }).format(amount);

View File

@ -1,13 +1,45 @@
import { ReactNode } from 'react';
/**
* Props for the EarningsCard component.
*/
interface EarningsCardProps {
/** Title displayed above the value (e.g., "Total Earnings") */
title: string;
/** Main value to display (e.g., "$1,500.00") */
value: string;
/** Icon element to display on the right side of the card */
icon: ReactNode;
/** Color theme for the card styling */
color: 'blue' | 'green' | 'yellow' | 'purple' | 'red';
/** Optional subtitle displayed below the value */
subtitle?: string;
}
/**
* Displays a styled card for showing earnings or commission statistics.
* Features a title, large value display, colored icon, and optional subtitle.
*
* @param props - The component props
* @param props.title - Label describing the statistic
* @param props.value - Formatted value to display prominently
* @param props.icon - React node for the icon (e.g., from lucide-react)
* @param props.color - Color theme affecting the value text and icon background
* @param props.subtitle - Additional context displayed below the value
*
* @example
* ```tsx
* import { DollarSign } from 'lucide-react';
*
* <EarningsCard
* title="Total Earnings"
* value="$12,500.00"
* icon={<DollarSign className="h-6 w-6" />}
* color="green"
* subtitle="Last 30 days"
* />
* ```
*/
export function EarningsCard({ title, value, icon, color, subtitle }: EarningsCardProps) {
const colorClasses = {
blue: 'bg-blue-100 text-blue-600',

View File

@ -2,17 +2,60 @@ import { ChevronLeft, ChevronRight } from 'lucide-react';
import { CommissionEntry } from '../../services/commissions/entries.api';
import { EntryStatusBadge } from './EntryStatusBadge';
/**
* Props for the EntriesList component.
*/
interface EntriesListProps {
/** Array of commission entries to display in the table */
entries: CommissionEntry[];
/** Total number of entries across all pages */
total: number;
/** Current page number (1-indexed) */
page: number;
/** Total number of pages available */
totalPages: number;
/** Array of selected entry IDs for bulk operations */
selectedEntries: string[];
/** Callback when selection changes (for bulk approve/reject) */
onSelectionChange: (ids: string[]) => void;
/** Callback when page changes for pagination */
onPageChange: (page: number) => void;
/** Callback to refresh the entries list */
onRefresh: () => void;
}
/**
* Displays a paginated table of commission entries with selection support.
* Allows selecting pending entries for bulk approval/rejection operations.
* Shows entry details including date, user, reference, amounts, and status.
*
* @param props - The component props
* @param props.entries - Commission entries to display
* @param props.total - Total entry count for pagination info
* @param props.page - Current page number
* @param props.totalPages - Total pages for pagination controls
* @param props.selectedEntries - Currently selected entry IDs
* @param props.onSelectionChange - Handler for selection updates
* @param props.onPageChange - Handler for page navigation
* @param props.onRefresh - Handler to refresh data after operations
*
* @example
* ```tsx
* const [selected, setSelected] = useState<string[]>([]);
* const [page, setPage] = useState(1);
*
* <EntriesList
* entries={commissionEntries}
* total={100}
* page={page}
* totalPages={10}
* selectedEntries={selected}
* onSelectionChange={setSelected}
* onPageChange={setPage}
* onRefresh={() => refetch()}
* />
* ```
*/
export function EntriesList({
entries,
total,

View File

@ -1,9 +1,32 @@
import { EntryStatus } from '../../services/commissions/entries.api';
/**
* Props for the EntryStatusBadge component.
*/
interface EntryStatusBadgeProps {
/** The status of the commission entry */
status: EntryStatus;
}
/**
* Renders a colored badge indicating the status of a commission entry.
* Each status has a distinct color for quick visual identification:
* - pending: yellow
* - approved: green
* - rejected: red
* - paid: blue
* - cancelled: gray
*
* @param props - The component props
* @param props.status - The entry status to display
*
* @example
* ```tsx
* <EntryStatusBadge status="pending" />
* <EntryStatusBadge status="approved" />
* <EntryStatusBadge status="paid" />
* ```
*/
export function EntryStatusBadge({ status }: EntryStatusBadgeProps) {
const getStatusConfig = (status: EntryStatus) => {
switch (status) {

View File

@ -2,15 +2,53 @@ import { ChevronLeft, ChevronRight, Lock, Unlock, DollarSign, Clock, CheckCircle
import { CommissionPeriod, PeriodStatus } from '../../services/commissions/periods.api';
import { useClosePeriod, useReopenPeriod, useMarkPeriodPaid } from '../../hooks/commissions';
/**
* Props for the PeriodManager component.
*/
interface PeriodManagerProps {
/** Array of commission periods to display */
periods: CommissionPeriod[];
/** Total number of periods across all pages */
total: number;
/** Current page number (1-indexed) */
page: number;
/** Total number of pages available */
totalPages: number;
/** Callback when page changes for pagination */
onPageChange: (page: number) => void;
/** Callback to refresh the periods list after operations */
onRefresh: () => void;
}
/**
* Manages commission periods with lifecycle operations.
* Displays periods in a table with their date ranges, entry counts, and totals.
* Provides actions to close, reopen, and mark periods as paid based on status.
*
* Period lifecycle: open -> closed -> processing -> paid
*
* @param props - The component props
* @param props.periods - Commission periods to display
* @param props.total - Total period count for pagination info
* @param props.page - Current page number
* @param props.totalPages - Total pages for pagination controls
* @param props.onPageChange - Handler for page navigation
* @param props.onRefresh - Handler to refresh data after operations
*
* @example
* ```tsx
* const [page, setPage] = useState(1);
*
* <PeriodManager
* periods={commissionPeriods}
* total={24}
* page={page}
* totalPages={3}
* onPageChange={setPage}
* onRefresh={() => refetch()}
* />
* ```
*/
export function PeriodManager({
periods,
total,

View File

@ -3,12 +3,50 @@ import { X, Plus, Trash2 } from 'lucide-react';
import { useCreateScheme, useUpdateScheme } from '../../hooks/commissions';
import { CreateSchemeDto, SchemeType, AppliesTo, TierConfig, CommissionScheme } from '../../services/commissions/schemes.api';
/**
* Props for the SchemeForm component.
*/
interface SchemeFormProps {
/** Existing scheme to edit, or undefined for creating a new scheme */
scheme?: CommissionScheme;
/** Callback when the form is closed/cancelled */
onClose: () => void;
/** Callback when the scheme is successfully created/updated */
onSuccess: () => void;
}
/**
* Modal form for creating or editing commission schemes.
* Supports three scheme types: percentage, fixed amount, and tiered.
* Includes configuration for rate, amounts, tiers, application scope, and limits.
*
* @param props - The component props
* @param props.scheme - Existing scheme for editing (omit for create mode)
* @param props.onClose - Handler to close the modal
* @param props.onSuccess - Handler called after successful save
*
* @example
* ```tsx
* // Create mode
* <SchemeForm
* onClose={() => setShowForm(false)}
* onSuccess={() => {
* setShowForm(false);
* refetch();
* }}
* />
*
* // Edit mode
* <SchemeForm
* scheme={existingScheme}
* onClose={() => setShowForm(false)}
* onSuccess={() => {
* setShowForm(false);
* refetch();
* }}
* />
* ```
*/
export function SchemeForm({ scheme, onClose, onSuccess }: SchemeFormProps) {
const isEditing = !!scheme;
const createMutation = useCreateScheme();

View File

@ -2,15 +2,51 @@ import { Pencil, Trash2, Copy, ToggleLeft, ToggleRight, ChevronLeft, ChevronRigh
import { CommissionScheme } from '../../services/commissions/schemes.api';
import { useDeleteScheme, useDuplicateScheme, useToggleSchemeActive } from '../../hooks/commissions';
/**
* Props for the SchemesList component.
*/
interface SchemesListProps {
/** Array of commission schemes to display in the table */
schemes: CommissionScheme[];
/** Total number of schemes across all pages */
total: number;
/** Current page number (1-indexed) */
page: number;
/** Total number of pages available */
totalPages: number;
/** Callback when page changes for pagination */
onPageChange: (page: number) => void;
/** Callback to refresh the schemes list after operations */
onRefresh: () => void;
}
/**
* Displays a paginated table of commission schemes with management actions.
* Shows scheme details including name, type, rate/amount, application scope, and status.
* Provides actions to toggle active status, duplicate, and delete schemes.
*
* @param props - The component props
* @param props.schemes - Commission schemes to display
* @param props.total - Total scheme count for pagination info
* @param props.page - Current page number
* @param props.totalPages - Total pages for pagination controls
* @param props.onPageChange - Handler for page navigation
* @param props.onRefresh - Handler to refresh data after operations
*
* @example
* ```tsx
* const [page, setPage] = useState(1);
*
* <SchemesList
* schemes={commissionSchemes}
* total={15}
* page={page}
* totalPages={2}
* onPageChange={setPage}
* onRefresh={() => refetch()}
* />
* ```
*/
export function SchemesList({
schemes,
total,

View File

@ -9,14 +9,59 @@ import { MoreVertical, Pencil, Trash2, ToggleLeft, ToggleRight, Copy } from 'luc
import { useState, useRef, useEffect } from 'react';
import clsx from 'clsx';
/**
* Props for the FeatureFlagCard component.
*/
interface FeatureFlagCardProps {
/** The feature flag data to display */
flag: FeatureFlag;
/** Callback invoked when the edit action is triggered */
onEdit: (flag: FeatureFlag) => void;
/** Callback invoked when the delete action is triggered */
onDelete: (flag: FeatureFlag) => void;
/** Callback invoked when the toggle action is triggered */
onToggle: (flag: FeatureFlag) => void;
/** Indicates if the toggle operation is in progress */
isToggling?: boolean;
}
/**
* Displays a feature flag as a card with its details, status, and action menu.
*
* @description Renders a card showing the feature flag's name, key, description,
* type, scope, category, rollout percentage, and default value. Includes actions
* for toggling, editing, and deleting the flag, plus a copy-to-clipboard button
* for the flag key.
*
* @param props - The component props
* @param props.flag - The feature flag data to display
* @param props.onEdit - Callback invoked when the edit action is triggered
* @param props.onDelete - Callback invoked when the delete action is triggered
* @param props.onToggle - Callback invoked when the toggle action is triggered
* @param props.isToggling - Indicates if the toggle operation is in progress
*
* @example
* ```tsx
* <FeatureFlagCard
* flag={{
* id: '1',
* key: 'enable_dark_mode',
* name: 'Enable Dark Mode',
* description: 'Allows users to switch to dark theme',
* flag_type: 'boolean',
* scope: 'user',
* is_enabled: true,
* default_value: false,
* rollout_percentage: 100,
* category: 'UI',
* }}
* onEdit={(flag) => console.log('Edit', flag)}
* onDelete={(flag) => console.log('Delete', flag)}
* onToggle={(flag) => console.log('Toggle', flag)}
* isToggling={false}
* />
* ```
*/
export function FeatureFlagCard({
flag,
onEdit,

View File

@ -2,13 +2,21 @@ import { useState, useEffect } from 'react';
import { FeatureFlag, FlagType, FlagScope, CreateFlagRequest, UpdateFlagRequest } from '@/services/api';
import { Loader2 } from 'lucide-react';
/**
* Props for the FeatureFlagForm component.
*/
interface FeatureFlagFormProps {
/** Existing flag data for editing mode; omit for create mode */
flag?: FeatureFlag;
/** Callback invoked on form submission with the flag data */
onSubmit: (data: CreateFlagRequest | UpdateFlagRequest) => void;
/** Callback invoked when the cancel button is clicked */
onCancel: () => void;
/** Indicates if the submit operation is in progress */
isLoading?: boolean;
}
/** Available flag types for selection. */
const FLAG_TYPES: { value: FlagType; label: string }[] = [
{ value: 'boolean', label: 'Boolean' },
{ value: 'string', label: 'String' },
@ -16,6 +24,7 @@ const FLAG_TYPES: { value: FlagType; label: string }[] = [
{ value: 'json', label: 'JSON' },
];
/** Available flag scopes for selection. */
const FLAG_SCOPES: { value: FlagScope; label: string; description: string }[] = [
{ value: 'global', label: 'Global', description: 'Applies to all tenants and users' },
{ value: 'tenant', label: 'Tenant', description: 'Can be overridden per tenant' },
@ -23,6 +32,38 @@ const FLAG_SCOPES: { value: FlagScope; label: string; description: string }[] =
{ value: 'plan', label: 'Plan', description: 'Based on subscription plan' },
];
/**
* Form for creating or editing a feature flag.
*
* @description Renders a form with fields for all feature flag properties including
* key (create only), name, description, type, scope, default value, category,
* rollout percentage, and enabled status. Validates input and parses the default
* value based on the selected flag type.
*
* @param props - The component props
* @param props.flag - Existing flag data for editing mode; omit for create mode
* @param props.onSubmit - Callback invoked on form submission with the flag data
* @param props.onCancel - Callback invoked when the cancel button is clicked
* @param props.isLoading - Indicates if the submit operation is in progress
*
* @example
* ```tsx
* // Create mode
* <FeatureFlagForm
* onSubmit={(data) => createFlag(data)}
* onCancel={() => closeModal()}
* isLoading={isCreating}
* />
*
* // Edit mode
* <FeatureFlagForm
* flag={existingFlag}
* onSubmit={(data) => updateFlag(existingFlag.id, data)}
* onCancel={() => closeModal()}
* isLoading={isUpdating}
* />
* ```
*/
export function FeatureFlagForm({ flag, onSubmit, onCancel, isLoading }: FeatureFlagFormProps) {
const isEditing = !!flag;

View File

@ -3,14 +3,53 @@ import { FeatureFlag, TenantFlag } from '@/services/api';
import { Plus, Trash2, ToggleLeft, ToggleRight, AlertCircle } from 'lucide-react';
import clsx from 'clsx';
/**
* Props for the TenantOverridesPanel component.
*/
interface TenantOverridesPanelProps {
/** List of all available feature flags */
flags: FeatureFlag[];
/** List of current tenant-specific flag overrides */
overrides: TenantFlag[];
/** Callback invoked when adding a new override */
onAdd: (flagId: string, isEnabled: boolean, value?: any) => void;
/** Callback invoked when removing an existing override */
onRemove: (flagId: string) => void;
/** Indicates if an operation is in progress */
isLoading?: boolean;
}
/**
* Panel for managing tenant-specific feature flag overrides.
*
* @description Displays a list of tenant overrides with the ability to add new
* overrides and remove existing ones. Allows tenants to customize global feature
* flag settings for their organization. Only flags that don't already have an
* override are available for selection.
*
* @param props - The component props
* @param props.flags - List of all available feature flags
* @param props.overrides - List of current tenant-specific flag overrides
* @param props.onAdd - Callback invoked when adding a new override
* @param props.onRemove - Callback invoked when removing an existing override
* @param props.isLoading - Indicates if an operation is in progress
*
* @example
* ```tsx
* <TenantOverridesPanel
* flags={[
* { id: '1', key: 'enable_dark_mode', name: 'Enable Dark Mode', ... },
* { id: '2', key: 'beta_features', name: 'Beta Features', ... },
* ]}
* overrides={[
* { id: 'o1', flag_id: '1', is_enabled: true, ... },
* ]}
* onAdd={(flagId, isEnabled) => console.log('Add override', flagId, isEnabled)}
* onRemove={(flagId) => console.log('Remove override', flagId)}
* isLoading={false}
* />
* ```
*/
export function TenantOverridesPanel({
flags,
overrides,

View File

@ -1,10 +1,35 @@
import React from 'react';
import { useDevices, usePushNotifications, UserDevice } from '@/hooks/usePushNotifications';
/**
* Props for the DevicesManager component.
*/
interface DevicesManagerProps {
/**
* Whether to display the header section with title and description.
* @default true
*/
showHeader?: boolean;
}
/**
* Manages user devices registered for push notifications.
*
* @description Displays a list of devices registered to receive push notifications,
* allowing users to view device details, register new devices, and unregister existing ones.
* Shows device type icons, activity status, and last used timestamp for each device.
*
* @param props - The component props
* @param props.showHeader - Whether to display the header section with title and "Add device" button
*
* @example
* // Basic usage with header
* <DevicesManager />
*
* @example
* // Without header (for embedding in settings pages)
* <DevicesManager showHeader={false} />
*/
export function DevicesManager({ showHeader = true }: DevicesManagerProps) {
const { data: devices, isLoading, error, refetch } = useDevices();
const { unregisterDevice, isSubscribed, requestPermission, isLoading: pushLoading } = usePushNotifications();

View File

@ -4,6 +4,28 @@ import clsx from 'clsx';
import { useUnreadNotificationsCount } from '@/hooks/useData';
import { NotificationDrawer } from './NotificationDrawer';
/**
* A bell icon button that displays the unread notification count and opens the notification drawer.
*
* @description Renders a clickable bell icon that shows a badge with the unread notification count.
* When clicked, it opens the NotificationDrawer component as a slide-in panel.
* The bell icon color changes based on whether there are unread notifications.
*
* @example
* // Basic usage in a header/navbar
* <header>
* <nav>
* <NotificationBell />
* </nav>
* </header>
*
* @example
* // Usage in a toolbar
* <div className="toolbar">
* <NotificationBell />
* <UserMenu />
* </div>
*/
export function NotificationBell() {
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const { data } = useUnreadNotificationsCount();

View File

@ -4,11 +4,41 @@ import clsx from 'clsx';
import { useNotifications, useMarkNotificationAsRead, useMarkAllNotificationsAsRead } from '@/hooks/useData';
import { NotificationItem } from './NotificationItem';
/**
* Props for the NotificationDrawer component.
*/
interface NotificationDrawerProps {
/**
* Controls the visibility of the drawer.
*/
isOpen: boolean;
/**
* Callback function invoked when the drawer should be closed.
*/
onClose: () => void;
}
/**
* A slide-in drawer panel that displays user notifications.
*
* @description Renders a full-height drawer that slides in from the right side of the screen.
* Displays a list of notifications with the ability to mark individual or all notifications as read.
* Includes a backdrop overlay, loading state, and empty state handling.
*
* @param props - The component props
* @param props.isOpen - Whether the drawer is currently open/visible
* @param props.onClose - Callback to close the drawer (triggered by backdrop click or close button)
*
* @example
* // Controlled drawer usage
* const [isOpen, setIsOpen] = useState(false);
*
* <button onClick={() => setIsOpen(true)}>Open Notifications</button>
* <NotificationDrawer
* isOpen={isOpen}
* onClose={() => setIsOpen(false)}
* />
*/
export function NotificationDrawer({ isOpen, onClose }: NotificationDrawerProps) {
const navigate = useNavigate();
const { data, isLoading } = useNotifications(1, 20);

View File

@ -3,9 +3,24 @@ import { Bell, AlertCircle, CheckCircle, Info, AlertTriangle } from 'lucide-reac
import clsx from 'clsx';
import type { Notification } from '@/hooks/useData';
/**
* Props for the NotificationItem component.
*/
interface NotificationItemProps {
/**
* The notification object to display.
*/
notification: Notification;
/**
* Callback invoked when the notification is clicked to mark it as read.
* @param id - The notification ID
*/
onRead: (id: string) => void;
/**
* Optional callback invoked when the notification has an action URL.
* If provided and the notification has an action_url, navigation will occur.
* @param url - The URL to navigate to
*/
onNavigate?: (url: string) => void;
}
@ -25,6 +40,33 @@ const typeColors: Record<string, string> = {
default: 'text-secondary-500 bg-secondary-50 dark:bg-secondary-800',
};
/**
* Renders a single notification item with icon, title, message, and timestamp.
*
* @description Displays a notification with type-based icon and coloring (info, success, warning, error).
* Shows read/unread status visually and handles click events to mark as read and optionally navigate.
* Uses relative time formatting for the timestamp (e.g., "5 minutes ago").
*
* @param props - The component props
* @param props.notification - The notification data including id, title, message, type, and timestamps
* @param props.onRead - Callback to mark the notification as read when clicked
* @param props.onNavigate - Optional callback for navigation when notification has an action URL
*
* @example
* // Basic usage
* <NotificationItem
* notification={{
* id: '1',
* title: 'New message',
* message: 'You have a new message from John',
* type: 'info',
* created_at: '2026-01-25T10:00:00Z',
* read_at: null
* }}
* onRead={(id) => markAsRead(id)}
* onNavigate={(url) => navigate(url)}
* />
*/
export function NotificationItem({ notification, onRead, onNavigate }: NotificationItemProps) {
const isRead = !!notification.read_at;
const Icon = typeIcons[notification.type] || typeIcons.default;

View File

@ -1,10 +1,43 @@
import React from 'react';
import { usePushNotifications } from '@/hooks/usePushNotifications';
/**
* Props for the PushPermissionBanner component.
*/
interface PushPermissionBannerProps {
/**
* Optional callback invoked when the user dismisses the banner.
* The dismissal is also persisted to localStorage.
*/
onDismiss?: () => void;
}
/**
* A promotional banner prompting users to enable push notifications.
*
* @description Displays a banner encouraging users to enable browser push notifications.
* Automatically hides when: push notifications are not supported, already subscribed,
* permission was denied, or the banner was previously dismissed. Stores dismissal
* state in localStorage to avoid showing repeatedly.
*
* @param props - The component props
* @param props.onDismiss - Optional callback when user dismisses the banner (via "Not now" or close button)
*
* @example
* // Basic usage at the top of a dashboard
* <div className="dashboard">
* <PushPermissionBanner />
* <DashboardContent />
* </div>
*
* @example
* // With custom dismiss handling
* <PushPermissionBanner
* onDismiss={() => {
* analytics.track('push_banner_dismissed');
* }}
* />
*/
export function PushPermissionBanner({ onDismiss }: PushPermissionBannerProps) {
const {
isSupported,

View File

@ -3,12 +3,45 @@ import { X } from 'lucide-react';
import { useCreateActivity } from '../../hooks/sales';
import { CreateActivityDto, ActivityType } from '../../services/sales/activities.api';
/**
* Props for the ActivityForm component
*/
interface ActivityFormProps {
/** Optional lead ID to associate the activity with */
leadId?: string;
/** Optional opportunity ID to associate the activity with */
opportunityId?: string;
/** Callback function triggered when the form is closed or after successful submission */
onClose: () => void;
}
/**
* Modal form component for creating sales activities such as calls, meetings, tasks, emails, or notes.
* Provides fields for activity type, subject, description, due date, and duration.
* Shows conditional fields based on activity type (e.g., call direction for calls, location for meetings).
* Activities can be associated with a lead, opportunity, or both.
*
* @param props - The component props
* @param props.leadId - Optional lead ID to link the activity
* @param props.opportunityId - Optional opportunity ID to link the activity
* @param props.onClose - Callback to close the modal
* @returns A modal form for activity creation
*
* @example
* ```tsx
* // Activity for a lead
* <ActivityForm
* leadId="lead-123"
* onClose={() => setShowForm(false)}
* />
*
* // Activity for an opportunity
* <ActivityForm
* opportunityId="opp-456"
* onClose={() => setShowForm(false)}
* />
* ```
*/
export function ActivityForm({ leadId, opportunityId, onClose }: ActivityFormProps) {
const [formData, setFormData] = useState<CreateActivityDto>({
type: 'task',

View File

@ -1,10 +1,48 @@
import { Phone, Users, CheckSquare, Mail, FileText, Clock, CheckCircle } from 'lucide-react';
import { Activity, ActivityType, ActivityStatus } from '../../services/sales/activities.api';
/**
* Props for the ActivityTimeline component
*/
interface ActivityTimelineProps {
/** Array of activity objects to display in chronological order */
activities: Activity[];
}
/**
* Displays a vertical timeline of sales activities with visual differentiation by type.
* Each activity shows an icon based on type (call, meeting, task, email, note),
* subject, description, status indicator, date, and optional outcome.
* Uses color-coded icons and a connecting timeline line for visual hierarchy.
*
* @param props - The component props
* @param props.activities - Array of activities to display in the timeline
* @returns A timeline component showing activity history
*
* @example
* ```tsx
* <ActivityTimeline
* activities={[
* {
* id: '1',
* type: 'call',
* subject: 'Follow-up call',
* status: 'completed',
* created_at: '2026-01-15',
* outcome: 'Scheduled demo for next week'
* },
* {
* id: '2',
* type: 'meeting',
* subject: 'Product demo',
* status: 'pending',
* due_date: '2026-01-22',
* created_at: '2026-01-15'
* }
* ]}
* />
* ```
*/
export function ActivityTimeline({ activities }: ActivityTimelineProps) {
const getIcon = (type: ActivityType) => {
switch (type) {

View File

@ -1,9 +1,37 @@
import { ConversionRates } from '../../services/sales/dashboard.api';
/**
* Props for the ConversionFunnel component
*/
interface ConversionFunnelProps {
/** Conversion rate data including stage-to-stage rates and breakdown by source */
conversion: ConversionRates;
}
/**
* Displays a visual sales conversion funnel with stage breakdown and rates.
* Shows a tapered funnel visualization from Leads to Opportunities to Won deals.
* Includes conversion rate metrics and optional breakdown by lead source.
*
* @param props - The component props
* @param props.conversion - Conversion rate data for funnel visualization
* @returns A funnel chart component with conversion metrics
*
* @example
* ```tsx
* <ConversionFunnel
* conversion={{
* leadToOpportunity: 30,
* opportunityToWon: 40,
* overall: 12,
* bySource: [
* { source: 'website', rate: 15 },
* { source: 'referral', rate: 25 }
* ]
* }}
* />
* ```
*/
export function ConversionFunnel({ conversion }: ConversionFunnelProps) {
const funnelStages = [
{ name: 'Leads', value: 100, color: 'bg-blue-500' },

View File

@ -1,10 +1,39 @@
import { Lead } from '../../services/sales/leads.api';
import { Mail, Phone, Building, Briefcase, Globe, MapPin, Star } from 'lucide-react';
/**
* Props for the LeadCard component
*/
interface LeadCardProps {
/** The lead data object to display */
lead: Lead;
}
/**
* Displays detailed information about a sales lead in a card format.
* Shows contact information, company details, address, lead status, source, and score.
*
* @param props - The component props
* @param props.lead - The lead data object containing all lead information
* @returns A card component displaying the lead's complete details
*
* @example
* ```tsx
* <LeadCard
* lead={{
* id: '123',
* first_name: 'John',
* last_name: 'Doe',
* email: 'john@example.com',
* status: 'qualified',
* source: 'website',
* score: 85,
* created_at: '2026-01-01',
* updated_at: '2026-01-15'
* }}
* />
* ```
*/
export function LeadCard({ lead }: LeadCardProps) {
const getStatusColor = (status: string) => {
switch (status) {

View File

@ -3,11 +3,38 @@ import { X } from 'lucide-react';
import { useCreateLead, useUpdateLead } from '../../hooks/sales';
import { Lead, CreateLeadDto, LeadSource, LeadStatus } from '../../services/sales/leads.api';
/**
* Props for the LeadForm component
*/
interface LeadFormProps {
/** Optional existing lead data for edit mode. If not provided, form operates in create mode */
lead?: Lead;
/** Callback function triggered when the form is closed or after successful submission */
onClose: () => void;
}
/**
* Modal form component for creating or editing sales leads.
* Provides fields for lead contact information, company details, source, status, and notes.
* Handles form validation and submission via React Query mutations.
*
* @param props - The component props
* @param props.lead - Optional existing lead data for editing
* @param props.onClose - Callback to close the modal
* @returns A modal form for lead creation/editing
*
* @example
* ```tsx
* // Create mode
* <LeadForm onClose={() => setShowForm(false)} />
*
* // Edit mode
* <LeadForm
* lead={existingLead}
* onClose={() => setShowForm(false)}
* />
* ```
*/
export function LeadForm({ lead, onClose }: LeadFormProps) {
const [formData, setFormData] = useState<CreateLeadDto>({
first_name: lead?.first_name || '',

View File

@ -1,14 +1,46 @@
import { useNavigate } from 'react-router-dom';
import { Lead } from '../../services/sales/leads.api';
/**
* Props for the LeadsList component
*/
interface LeadsListProps {
/** Array of lead objects to display in the table */
leads: Lead[];
/** Total number of leads across all pages */
total: number;
/** Current page number (1-indexed) */
page: number;
/** Total number of available pages */
totalPages: number;
/** Callback function triggered when user navigates to a different page */
onPageChange: (page: number) => void;
}
/**
* Displays a paginated table of sales leads with navigation support.
* Shows lead name, company, status, source, score, and creation date.
* Clicking a row navigates to the lead detail page.
*
* @param props - The component props
* @param props.leads - Array of leads to display
* @param props.total - Total lead count for pagination info
* @param props.page - Current page number
* @param props.totalPages - Total pages available
* @param props.onPageChange - Page navigation callback
* @returns A table component with pagination controls
*
* @example
* ```tsx
* <LeadsList
* leads={leadsData}
* total={100}
* page={1}
* totalPages={5}
* onPageChange={(newPage) => setCurrentPage(newPage)}
* />
* ```
*/
export function LeadsList({ leads, total, page, totalPages, onPageChange }: LeadsListProps) {
const navigate = useNavigate();

View File

@ -1,12 +1,45 @@
import { DollarSign, Calendar, User } from 'lucide-react';
import { Opportunity } from '../../services/sales/opportunities.api';
/**
* Props for the OpportunityCard component
*/
interface OpportunityCardProps {
/** The opportunity data object to display */
opportunity: Opportunity;
/** Whether to render in compact mode with minimal information. Defaults to false */
compact?: boolean;
/** Optional click handler for card interaction */
onClick?: () => void;
}
/**
* Displays a sales opportunity in a card format with two display modes.
* Full mode shows name, company, amount, probability, close date, and contact.
* Compact mode shows only name and amount for use in dense layouts like pipeline boards.
*
* @param props - The component props
* @param props.opportunity - The opportunity data to display
* @param props.compact - Enable compact display mode
* @param props.onClick - Click handler for card interaction
* @returns A card component displaying opportunity information
*
* @example
* ```tsx
* // Full display mode
* <OpportunityCard
* opportunity={opportunityData}
* onClick={() => navigate(`/opportunities/${opportunityData.id}`)}
* />
*
* // Compact mode for pipeline boards
* <OpportunityCard
* opportunity={opportunityData}
* compact
* onClick={() => handleSelect(opportunityData)}
* />
* ```
*/
export function OpportunityCard({ opportunity, compact = false, onClick }: OpportunityCardProps) {
const formatCurrency = (amount: number) => {
return new Intl.NumberFormat('en-US', {

View File

@ -3,11 +3,39 @@ import { X } from 'lucide-react';
import { useCreateOpportunity, useUpdateOpportunity } from '../../hooks/sales';
import { Opportunity, CreateOpportunityDto, OpportunityStage } from '../../services/sales/opportunities.api';
/**
* Props for the OpportunityForm component
*/
interface OpportunityFormProps {
/** Optional existing opportunity data for edit mode. If not provided, form operates in create mode */
opportunity?: Opportunity;
/** Callback function triggered when the form is closed or after successful submission */
onClose: () => void;
}
/**
* Modal form component for creating or editing sales opportunities.
* Provides fields for opportunity name, amount, currency, probability, stage,
* expected close date, contact information, and notes.
* Handles form validation and submission via React Query mutations.
*
* @param props - The component props
* @param props.opportunity - Optional existing opportunity data for editing
* @param props.onClose - Callback to close the modal
* @returns A modal form for opportunity creation/editing
*
* @example
* ```tsx
* // Create mode
* <OpportunityForm onClose={() => setShowForm(false)} />
*
* // Edit mode
* <OpportunityForm
* opportunity={existingOpportunity}
* onClose={() => setShowForm(false)}
* />
* ```
*/
export function OpportunityForm({ opportunity, onClose }: OpportunityFormProps) {
const [formData, setFormData] = useState<CreateOpportunityDto>({
name: opportunity?.name || '',

View File

@ -2,10 +2,40 @@ import { useNavigate } from 'react-router-dom';
import { PipelineView } from '../../services/sales/opportunities.api';
import { OpportunityCard } from './OpportunityCard';
/**
* Props for the PipelineBoard component
*/
interface PipelineBoardProps {
/** Array of pipeline stages with their opportunities, counts, and totals */
stages: PipelineView[];
}
/**
* Displays a Kanban-style sales pipeline board with opportunities organized by stage.
* Open stages (prospecting, qualification, proposal, negotiation) are shown as columns.
* Closed stages (won/lost) are displayed in a separate grid below.
* Each column shows stage name, opportunity count, and total value.
*
* @param props - The component props
* @param props.stages - Array of pipeline stage data with opportunities
* @returns A Kanban board component for visualizing the sales pipeline
*
* @example
* ```tsx
* <PipelineBoard
* stages={[
* {
* stage: 'prospecting',
* stageName: 'Prospecting',
* count: 5,
* totalAmount: 50000,
* opportunities: [...]
* },
* // ... other stages
* ]}
* />
* ```
*/
export function PipelineBoard({ stages }: PipelineBoardProps) {
const navigate = useNavigate();

View File

@ -2,11 +2,44 @@ import { TrendingUp, Users, DollarSign, Target, ArrowUpRight, ArrowDownRight } f
import { SalesSummary, ConversionRates } from '../../services/sales/dashboard.api';
import { ConversionFunnel } from './ConversionFunnel';
/**
* Props for the SalesDashboard component
*/
interface SalesDashboardProps {
/** Sales summary data including leads, opportunities, pipeline, and activities metrics */
summary: SalesSummary;
/** Conversion rate data for the sales funnel visualization */
conversion: ConversionRates;
}
/**
* Displays a comprehensive sales dashboard with key metrics and visualizations.
* Includes stat cards for total leads, open opportunities, won value, and win rate.
* Shows pipeline breakdown by stage, conversion funnel, and activities overview.
*
* @param props - The component props
* @param props.summary - Sales summary metrics data
* @param props.conversion - Conversion rates for funnel visualization
* @returns A dashboard component with sales KPIs and charts
*
* @example
* ```tsx
* <SalesDashboard
* summary={{
* leads: { total: 150, conversionRate: 25 },
* opportunities: { open: 45, won: 12, wonValue: 125000, winRate: 35, avgDealSize: 10000 },
* pipeline: { totalValue: 500000, byStage: [...] },
* activities: { total: 200, pending: 30, completed: 160, overdue: 10 }
* }}
* conversion={{
* leadToOpportunity: 30,
* opportunityToWon: 40,
* overall: 12,
* bySource: [...]
* }}
* />
* ```
*/
export function SalesDashboard({ summary, conversion }: SalesDashboardProps) {
const formatCurrency = (amount: number) => {
return new Intl.NumberFormat('en-US', {

View File

@ -15,10 +15,17 @@ import {
import { StorageFile, storageApi } from '@/services/api';
import { useDeleteFile } from '@/hooks/useStorage';
/**
* Props for the FileItem component.
*/
interface FileItemProps {
/** The storage file object containing metadata and content info */
file: StorageFile;
/** Display mode: 'grid' for thumbnail cards, 'list' for horizontal rows */
view?: 'grid' | 'list';
/** Callback fired after successful file deletion */
onDelete?: () => void;
/** Callback fired when user requests file preview (images only) */
onPreview?: (file: StorageFile) => void;
}
@ -56,6 +63,32 @@ function VisibilityIcon({ visibility }: { visibility: string }) {
}
}
/**
* Displays a single file entry with download, delete, and preview actions.
* Supports both grid (thumbnail cards) and list (horizontal rows) view modes.
* Automatically shows appropriate icons based on file MIME type and handles
* visibility indicators (private, tenant, public).
*
* @description Renders a file item with actions for download, delete, and preview.
* @param props - The component props
* @param props.file - The storage file object to display
* @param props.view - Display mode: 'grid' or 'list' (default: 'list')
* @param props.onDelete - Optional callback after successful deletion
* @param props.onPreview - Optional callback for image preview requests
*
* @example
* // Basic usage in list view
* <FileItem file={myFile} />
*
* @example
* // Grid view with preview handler
* <FileItem
* file={myFile}
* view="grid"
* onPreview={(file) => openPreviewModal(file)}
* onDelete={() => refetchFiles()}
* />
*/
export function FileItem({ file, view = 'list', onDelete, onPreview }: FileItemProps) {
const [showMenu, setShowMenu] = useState(false);
const [downloading, setDownloading] = useState(false);

View File

@ -4,12 +4,41 @@ import { useFiles } from '@/hooks/useStorage';
import { StorageFile } from '@/services/api';
import { FileItem } from './FileItem';
/**
* Props for the FileList component.
*/
interface FileListProps {
/** Optional folder path to filter files */
folder?: string;
/** Callback fired when user requests file preview */
onPreview?: (file: StorageFile) => void;
/** Additional CSS classes to apply to the container */
className?: string;
}
/**
* Displays a paginated, searchable list of files with grid/list view toggle.
* Fetches files from the storage API and provides search, pagination,
* and view mode switching. Handles loading, empty, and error states.
*
* @description Renders a file browser with search, pagination, and view modes.
* @param props - The component props
* @param props.folder - Optional folder to filter files by
* @param props.onPreview - Optional callback for file preview requests
* @param props.className - Additional CSS classes for the container
*
* @example
* // Basic usage
* <FileList />
*
* @example
* // With folder filter and preview handler
* <FileList
* folder="documents"
* onPreview={(file) => setPreviewFile(file)}
* className="mt-4"
* />
*/
export function FileList({ folder, onPreview, className = '' }: FileListProps) {
const [view, setView] = useState<'grid' | 'list'>('list');
const [page, setPage] = useState(1);

View File

@ -2,13 +2,23 @@ import { useState, useRef, useCallback } from 'react';
import { Upload, X, File, Image, FileText, Table, AlertCircle } from 'lucide-react';
import { useUploadFile } from '@/hooks/useStorage';
/**
* Props for the FileUpload component.
*/
interface FileUploadProps {
/** Target folder for uploaded files (default: 'files') */
folder?: string;
/** File visibility level: 'private', 'tenant', or 'public' (default: 'private') */
visibility?: 'private' | 'tenant' | 'public';
/** MIME types to accept (defaults to ALLOWED_TYPES) */
accept?: string;
maxSize?: number; // in bytes
/** Maximum file size in bytes (default: 50MB) */
maxSize?: number;
/** Callback fired after successful upload with file id and filename */
onSuccess?: (file: { id: string; filename: string }) => void;
/** Callback fired when upload fails with the error */
onError?: (error: Error) => void;
/** Additional CSS classes to apply to the container */
className?: string;
}
@ -42,6 +52,37 @@ function formatBytes(bytes: number): string {
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
}
/**
* Drag-and-drop file upload component with progress indicator.
* Supports file validation (type and size), visual drag feedback,
* upload progress display, and error handling. Files can be selected
* via drag-and-drop or click-to-browse.
*
* @description Renders a drag-and-drop zone for uploading files with progress.
* @param props - The component props
* @param props.folder - Target folder for uploads (default: 'files')
* @param props.visibility - File visibility: 'private', 'tenant', or 'public'
* @param props.accept - MIME types to accept
* @param props.maxSize - Maximum file size in bytes (default: 50MB)
* @param props.onSuccess - Callback after successful upload
* @param props.onError - Callback on upload error
* @param props.className - Additional CSS classes
*
* @example
* // Basic usage
* <FileUpload onSuccess={(file) => console.log('Uploaded:', file.id)} />
*
* @example
* // With custom settings
* <FileUpload
* folder="avatars"
* visibility="public"
* maxSize={5 * 1024 * 1024}
* accept="image/*"
* onSuccess={handleUpload}
* onError={(err) => toast.error(err.message)}
* />
*/
export function FileUpload({
folder = 'files',
visibility = 'private',

View File

@ -1,7 +1,11 @@
import { HardDrive, Folder } from 'lucide-react';
import { useStorageUsage } from '@/hooks/useStorage';
/**
* Props for the StorageUsageCard component.
*/
interface StorageUsageCardProps {
/** Additional CSS classes to apply to the card container */
className?: string;
}
@ -12,6 +16,24 @@ function formatBytes(bytes: number): string {
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
}
/**
* Displays storage usage statistics in a card format.
* Shows total bytes used, usage percentage with color-coded progress bar,
* file count, max file size, and breakdown by folder. Handles loading
* and error states gracefully.
*
* @description Renders a card showing storage quota usage and file statistics.
* @param props - The component props
* @param props.className - Additional CSS classes for the card container
*
* @example
* // Basic usage
* <StorageUsageCard />
*
* @example
* // With custom styling
* <StorageUsageCard className="col-span-2 shadow-lg" />
*/
export function StorageUsageCard({ className = '' }: StorageUsageCardProps) {
const { data, isLoading, error } = useStorageUsage();

View File

@ -14,15 +14,50 @@ import {
} from 'lucide-react';
import clsx from 'clsx';
/**
* Props for the WebhookCard component.
*/
interface WebhookCardProps {
/** The webhook data to display */
webhook: Webhook;
/** Callback invoked when the user clicks the edit action */
onEdit: (webhook: Webhook) => void;
/** Callback invoked when the user clicks the delete action */
onDelete: (webhook: Webhook) => void;
/** Callback invoked when the user clicks the test/send action */
onTest: (webhook: Webhook) => void;
/** Callback invoked when the user toggles the webhook active state */
onToggle: (webhook: Webhook) => void;
/** Callback invoked when the user wants to view delivery history */
onViewDeliveries: (webhook: Webhook) => void;
}
/**
* Displays a single webhook configuration as a card with status, events, and statistics.
*
* @description Renders a card showing webhook details including name, URL, subscribed events,
* delivery statistics, and a dropdown menu with actions (edit, test, view deliveries, toggle, delete).
*
* @param props - The component props
* @param props.webhook - The webhook data to display
* @param props.onEdit - Callback when user clicks edit
* @param props.onDelete - Callback when user clicks delete
* @param props.onTest - Callback when user clicks send test
* @param props.onToggle - Callback when user toggles active state
* @param props.onViewDeliveries - Callback when user clicks view deliveries
*
* @example
* ```tsx
* <WebhookCard
* webhook={webhookData}
* onEdit={(wh) => openEditModal(wh)}
* onDelete={(wh) => confirmDelete(wh)}
* onTest={(wh) => sendTestWebhook(wh)}
* onToggle={(wh) => toggleWebhookStatus(wh)}
* onViewDeliveries={(wh) => showDeliveryHistory(wh)}
* />
* ```
*/
export function WebhookCard({
webhook,
onEdit,

View File

@ -11,11 +11,19 @@ import {
} from 'lucide-react';
import clsx from 'clsx';
/**
* Props for the WebhookDeliveryList component.
*/
interface WebhookDeliveryListProps {
/** Array of webhook delivery records to display */
deliveries: WebhookDelivery[];
/** Whether the list is currently loading data */
isLoading?: boolean;
/** Callback invoked when the user requests to retry a failed delivery */
onRetry: (deliveryId: string) => void;
/** Callback invoked when the user requests to load more deliveries (pagination) */
onLoadMore?: () => void;
/** Indicates whether there are more deliveries available to load */
hasMore?: boolean;
}
@ -45,6 +53,16 @@ const statusConfig: Record<
},
};
/**
* Renders a single delivery record as an expandable row.
*
* @description Displays delivery summary (event type, timestamp, status) with expand/collapse
* functionality to show detailed information including payload, response, and retry options.
*
* @param props - The component props
* @param props.delivery - The webhook delivery record to display
* @param props.onRetry - Callback when user clicks retry on a failed delivery
*/
function DeliveryRow({
delivery,
onRetry,
@ -183,6 +201,31 @@ function DeliveryRow({
);
}
/**
* Displays a paginated list of webhook delivery attempts with their status and details.
*
* @description Renders a list of delivery records showing status (delivered, failed, pending, retrying),
* event type, timestamps, payload data, and response information. Supports pagination via "Load More"
* and allows retrying failed deliveries.
*
* @param props - The component props
* @param props.deliveries - Array of webhook delivery records to display
* @param props.isLoading - Whether data is currently being loaded
* @param props.onRetry - Callback when user requests to retry a failed delivery
* @param props.onLoadMore - Optional callback to load more deliveries (pagination)
* @param props.hasMore - Whether there are more deliveries available to load
*
* @example
* ```tsx
* <WebhookDeliveryList
* deliveries={webhookDeliveries}
* isLoading={isLoadingDeliveries}
* onRetry={(deliveryId) => retryDelivery(deliveryId)}
* onLoadMore={() => fetchMoreDeliveries()}
* hasMore={hasMoreDeliveries}
* />
* ```
*/
export function WebhookDeliveryList({
deliveries,
isLoading,

View File

@ -3,14 +3,58 @@ import { Webhook, CreateWebhookRequest, UpdateWebhookRequest, WebhookEvent } fro
import { X, Plus, Trash2, Eye, EyeOff, Copy, Check } from 'lucide-react';
import clsx from 'clsx';
/**
* Props for the WebhookForm component.
*/
interface WebhookFormProps {
/** Existing webhook data for editing, or null/undefined for creating a new webhook */
webhook?: Webhook | null;
/** List of available webhook events that can be subscribed to */
events: WebhookEvent[];
/** Callback invoked when the form is submitted with valid data */
onSubmit: (data: CreateWebhookRequest | UpdateWebhookRequest) => void;
/** Callback invoked when the user cancels the form */
onCancel: () => void;
/** Whether the form submission is in progress */
isLoading?: boolean;
}
/**
* Form component for creating or editing webhook configurations.
*
* @description Provides a comprehensive form for webhook management including:
* - Name and description fields
* - HTTPS endpoint URL with validation
* - Event selection checkboxes
* - Custom HTTP headers management
* - Signing secret display (edit mode only)
*
* @param props - The component props
* @param props.webhook - Existing webhook data for editing, or null for creating
* @param props.events - List of available webhook events to subscribe to
* @param props.onSubmit - Callback when form is submitted with valid data
* @param props.onCancel - Callback when user cancels the form
* @param props.isLoading - Whether submission is in progress (disables form)
*
* @example
* ```tsx
* // Creating a new webhook
* <WebhookForm
* events={availableEvents}
* onSubmit={(data) => createWebhook(data)}
* onCancel={() => closeModal()}
* />
*
* // Editing an existing webhook
* <WebhookForm
* webhook={existingWebhook}
* events={availableEvents}
* onSubmit={(data) => updateWebhook(existingWebhook.id, data)}
* onCancel={() => closeModal()}
* isLoading={isSaving}
* />
* ```
*/
export function WebhookForm({
webhook,
events,

View File

@ -1,10 +1,46 @@
import React, { useState } from 'react';
import { useTestWhatsAppConnection } from '../../hooks/useWhatsApp';
/**
* Props for the WhatsAppTestMessage component.
*/
interface WhatsAppTestMessageProps {
/**
* When true, disables the input field and submit button.
* Useful when the WhatsApp integration is not configured.
* @default false
*/
disabled?: boolean;
}
/**
* A form component for testing WhatsApp Business API connectivity.
*
* Allows users to send a test message to a specified phone number in E.164 format
* to verify that the WhatsApp integration is working correctly.
*
* @description Renders a card with a phone number input and submit button.
* Uses the useTestWhatsAppConnection hook to send the test message.
* Shows loading state while the message is being sent.
*
* @param props - The component props
* @param props.disabled - Optional flag to disable the form controls
*
* @example
* // Basic usage
* <WhatsAppTestMessage />
*
* @example
* // Disabled state when integration is not configured
* <WhatsAppTestMessage disabled={true} />
*
* @example
* // Usage within a settings panel
* <div className="settings-panel">
* <WhatsAppConfig />
* <WhatsAppTestMessage disabled={!isConfigured} />
* </div>
*/
export function WhatsAppTestMessage({ disabled }: WhatsAppTestMessageProps) {
const [phoneNumber, setPhoneNumber] = useState('');
const testConnection = useTestWhatsAppConnection();

View File

@ -30,6 +30,21 @@ import { TenantsPage, TenantDetailPage, MetricsPage } from '@/pages/superadmin';
// Onboarding
import { OnboardingPage } from '@/pages/onboarding';
// Sales pages
import SalesPage from '@/pages/sales';
import LeadsPage from '@/pages/sales/leads';
import LeadDetailPage from '@/pages/sales/leads/[id]';
import OpportunitiesPage from '@/pages/sales/opportunities';
import OpportunityDetailPage from '@/pages/sales/opportunities/[id]';
import ActivitiesPage from '@/pages/sales/activities';
// Commissions pages
import CommissionsPage from '@/pages/commissions';
import SchemesPage from '@/pages/commissions/schemes';
import EntriesPage from '@/pages/commissions/entries';
import PeriodsPage from '@/pages/commissions/periods';
import MyEarningsPage from '@/pages/commissions/my-earnings';
// Protected Route wrapper
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
@ -108,6 +123,21 @@ export function AppRouter() {
<Route path="audit" element={<AuditLogsPage />} />
<Route path="feature-flags" element={<FeatureFlagsPage />} />
<Route path="whatsapp" element={<WhatsAppSettings />} />
{/* Sales routes */}
<Route path="sales" element={<SalesPage />} />
<Route path="sales/leads" element={<LeadsPage />} />
<Route path="sales/leads/:id" element={<LeadDetailPage />} />
<Route path="sales/opportunities" element={<OpportunitiesPage />} />
<Route path="sales/opportunities/:id" element={<OpportunityDetailPage />} />
<Route path="sales/activities" element={<ActivitiesPage />} />
{/* Commissions routes */}
<Route path="commissions" element={<CommissionsPage />} />
<Route path="commissions/schemes" element={<SchemesPage />} />
<Route path="commissions/entries" element={<EntriesPage />} />
<Route path="commissions/periods" element={<PeriodsPage />} />
<Route path="commissions/my-earnings" element={<MyEarningsPage />} />
</Route>
{/* Superadmin routes */}

View File

@ -10,6 +10,11 @@ export interface User {
tenant_id: string;
}
export interface UpdateProfileData {
first_name?: string;
last_name?: string;
}
export interface AuthState {
user: User | null;
accessToken: string | null;
@ -23,6 +28,8 @@ export interface AuthState {
login: (user: User, accessToken: string, refreshToken: string) => void;
logout: () => void;
setLoading: (loading: boolean) => void;
refreshTokens: () => Promise<boolean>;
updateProfile: (data: UpdateProfileData) => void;
}
export const useAuthStore = create<AuthState>()(
@ -57,6 +64,57 @@ export const useAuthStore = create<AuthState>()(
}),
setLoading: (isLoading) => set({ isLoading }),
refreshTokens: async () => {
const state = useAuthStore.getState();
if (!state.refreshToken) {
return false;
}
try {
const response = await fetch(
`${import.meta.env.VITE_API_URL || '/api/v1'}/auth/refresh`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ refreshToken: state.refreshToken }),
}
);
if (!response.ok) {
throw new Error('Failed to refresh token');
}
const data = await response.json();
set({
accessToken: data.accessToken,
refreshToken: data.refreshToken,
});
return true;
} catch {
set({
user: null,
accessToken: null,
refreshToken: null,
isAuthenticated: false,
});
return false;
}
},
updateProfile: (data) => {
set((state) => ({
user: state.user
? {
...state.user,
...(data.first_name !== undefined && { first_name: data.first_name }),
...(data.last_name !== undefined && { last_name: data.last_name }),
}
: null,
}));
},
}),
{
name: 'auth-storage',

View File

@ -0,0 +1,804 @@
# Especificación de Páginas Frontend - Template SaaS
**Versión:** 1.0.0
**Actualizado:** 2026-01-25
**Total Páginas:** 38
---
## Índice
1. [Auth Pages](#1-auth-pages)
2. [Dashboard Pages](#2-dashboard-pages)
3. [Sales Pages](#3-sales-pages)
4. [Commissions Pages](#4-commissions-pages)
5. [Settings Pages](#5-settings-pages)
6. [Superadmin Pages](#6-superadmin-pages)
7. [Onboarding Pages](#7-onboarding-pages)
---
## 1. Auth Pages
### 1.1 LoginPage
**Ruta:** `/auth/login`
**Archivo:** `src/pages/auth/LoginPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Autenticación de usuarios existentes |
| **Layout** | AuthLayout |
| **Guard** | GuestRoute |
| **Componentes** | LoginForm, OAuthButtons |
| **Hooks** | useAuth, useForm |
| **APIs** | `authApi.login()` |
**Estados:**
- `idle` - Formulario disponible
- `loading` - Enviando credenciales
- `error` - Credenciales inválidas
- `success` - Redirige a `/dashboard`
**Campos:**
- Email (required, email format)
- Password (required, min 8 chars)
- Remember me (checkbox)
---
### 1.2 RegisterPage
**Ruta:** `/auth/register`
**Archivo:** `src/pages/auth/RegisterPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Registro de nuevos usuarios |
| **Layout** | AuthLayout |
| **Guard** | GuestRoute |
| **Componentes** | RegisterForm, OAuthButtons |
| **Hooks** | useAuth, useForm |
| **APIs** | `authApi.register()` |
**Estados:**
- `idle` - Formulario disponible
- `loading` - Creando cuenta
- `error` - Email duplicado u otro error
- `success` - Redirige a `/onboarding`
**Campos:**
- First name (optional)
- Last name (optional)
- Email (required, email format)
- Password (required, min 8 chars, complexity)
- Confirm password (must match)
- Terms acceptance (required)
---
### 1.3 ForgotPasswordPage
**Ruta:** `/auth/forgot-password`
**Archivo:** `src/pages/auth/ForgotPasswordPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Solicitar reset de contraseña |
| **Layout** | AuthLayout |
| **Guard** | GuestRoute |
| **Componentes** | Form básico |
| **Hooks** | useForm |
| **APIs** | `authApi.requestPasswordReset()` |
**Estados:**
- `idle` - Formulario disponible
- `loading` - Enviando solicitud
- `success` - Muestra mensaje de email enviado
- `error` - Email no encontrado
---
## 2. Dashboard Pages
### 2.1 DashboardPage
**Ruta:** `/dashboard`
**Archivo:** `src/pages/dashboard/DashboardPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Vista principal con métricas y acciones rápidas |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | MetricCard, TrendChart, QuickActions |
| **Hooks** | useAuth, useSubscription, useNotifications |
| **APIs** | `billingApi.getSummary()`, `notificationsApi.getUnreadCount()` |
**Secciones:**
- Welcome banner con nombre de usuario
- Métricas principales (usuarios, storage, API calls)
- Gráfico de actividad reciente
- Acciones rápidas
- Notificaciones recientes
---
### 2.2 BillingPage
**Ruta:** `/dashboard/billing`
**Archivo:** `src/pages/dashboard/BillingPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Gestión de suscripción y pagos |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | SubscriptionCard, InvoiceList, PaymentMethodCard |
| **Hooks** | useSubscription, useInvoices, useBillingPortal |
| **APIs** | `billingApi.*`, `stripeApi.*` |
**Secciones:**
- Plan actual y estado
- Uso del mes (límites)
- Métodos de pago
- Historial de facturas
- Botón "Manage Subscription" (Stripe Portal)
---
### 2.3 UsersPage
**Ruta:** `/dashboard/users`
**Archivo:** `src/pages/dashboard/UsersPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Gestión de usuarios del tenant |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | UserTable, InviteModal, RoleSelect |
| **Hooks** | useUsers, useRoles |
| **APIs** | `usersApi.list()`, `usersApi.invite()`, `usersApi.update()` |
**Acciones:**
- Listar usuarios
- Invitar nuevos usuarios
- Cambiar roles
- Desactivar usuarios
---
### 2.4 AIPage
**Ruta:** `/dashboard/ai`
**Archivo:** `src/pages/dashboard/AIPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Chat con LLM y configuración |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | AIChat, AISettings, ChatMessage |
| **Hooks** | useAI, useAIChat, useAIUsage |
| **APIs** | `aiApi.chat()`, `aiApi.getConfig()`, `aiApi.getCurrentUsage()` |
**Secciones:**
- Chat interface con historial
- Selector de modelo
- Uso actual (tokens, costo)
- Configuración (temperatura, max tokens)
---
### 2.5 StoragePage
**Ruta:** `/dashboard/storage`
**Archivo:** `src/pages/dashboard/StoragePage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Gestión de archivos |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | FileList, FileUpload, FileItem, StorageUsageCard |
| **Hooks** | useFiles, useUpload, useStorageUsage |
| **APIs** | `storageApi.listFiles()`, `storageApi.uploadFile()`, `storageApi.deleteFile()` |
**Acciones:**
- Subir archivos (drag & drop)
- Listar archivos con paginación
- Filtrar por carpeta/tipo
- Descargar archivos
- Eliminar archivos
- Ver uso de storage
---
### 2.6 WebhooksPage
**Ruta:** `/dashboard/webhooks`
**Archivo:** `src/pages/dashboard/WebhooksPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Configuración de webhooks outbound |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | WebhookCard, WebhookForm, WebhookDeliveryList |
| **Hooks** | useWebhooks, useWebhookDeliveries |
| **APIs** | `webhooksApi.*` |
**Acciones:**
- Crear webhook
- Editar URL, eventos, headers
- Ver historial de entregas
- Reintentar entregas fallidas
- Test webhook
---
### 2.7 AuditLogsPage
**Ruta:** `/dashboard/audit`
**Archivo:** `src/pages/dashboard/AuditLogsPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Registro de auditoría de acciones |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | AuditLogRow, AuditFilters, AuditStatsCard, ActivityTimeline |
| **Hooks** | useAuditLogs, useActivityLogs |
| **APIs** | `auditApi.queryLogs()`, `auditApi.getStats()` |
**Filtros:**
- Por usuario
- Por acción (create, update, delete, login)
- Por entidad
- Por fecha
---
### 2.8 FeatureFlagsPage
**Ruta:** `/dashboard/feature-flags`
**Archivo:** `src/pages/dashboard/FeatureFlagsPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Gestión de feature toggles |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | FeatureFlagCard, FeatureFlagForm, TenantOverridesPanel |
| **Hooks** | useFeatureFlags, useFeatureFlag |
| **APIs** | `featureFlagsApi.*` |
**Acciones:**
- Listar flags
- Crear/editar flags
- Toggle on/off
- Configurar overrides por tenant/usuario
- Ver targeting rules
---
### 2.9 WhatsAppSettings
**Ruta:** `/dashboard/whatsapp`
**Archivo:** `src/pages/admin/WhatsAppSettings.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Configuración WhatsApp Business API |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | WhatsAppTestMessage |
| **Hooks** | useWhatsApp |
| **APIs** | `whatsappApi.*` |
**Secciones:**
- Estado de conexión
- Configuración de API
- Envío de mensaje de prueba
- Templates de mensajes
---
## 3. Sales Pages
### 3.1 SalesPage (Dashboard)
**Ruta:** `/dashboard/sales`
**Archivo:** `src/pages/sales/index.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Dashboard de ventas con métricas |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | SalesDashboard |
| **Hooks** | useSalesSummary, useConversionRates |
| **APIs** | Sales Dashboard API |
**Métricas:**
- Total leads
- Conversion rate
- Pipeline value
- Deals cerrados
---
### 3.2 LeadsPage
**Ruta:** `/dashboard/sales/leads`
**Archivo:** `src/pages/sales/leads/index.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Listado y gestión de leads |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | LeadsList, LeadForm |
| **Hooks** | useLeads, useLeadStats |
| **APIs** | Leads API |
**Filtros:**
- Search (nombre, email, empresa)
- Status (new, contacted, qualified, unqualified, converted)
- Source (website, referral, cold_call, event, advertisement, social_media)
**Acciones:**
- Crear lead
- Ver detalle
- Filtrar y buscar
---
### 3.3 LeadDetailPage
**Ruta:** `/dashboard/sales/leads/:id`
**Archivo:** `src/pages/sales/leads/[id].tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Detalle y edición de un lead |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | LeadCard, LeadForm, ActivityTimeline, ActivityForm |
| **Hooks** | useLead, useDeleteLead, useConvertLead, useLeadActivities |
| **APIs** | Leads API, Activities API |
**Secciones:**
- Información del lead
- Score
- Información de contacto
- Timeline de actividades
- Notas
**Acciones:**
- Editar lead
- Eliminar lead
- Convertir a oportunidad
- Agregar actividad
---
### 3.4 OpportunitiesPage
**Ruta:** `/dashboard/sales/opportunities`
**Archivo:** `src/pages/sales/opportunities/index.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Pipeline de oportunidades (Kanban) |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | PipelineBoard, OpportunityForm |
| **Hooks** | usePipeline, useOpportunityStats |
| **APIs** | Pipeline API, Opportunities API |
**Vistas:**
- Pipeline (Kanban)
- List view
**Métricas:**
- Open
- Won
- Win Rate
- Avg Deal Size
---
### 3.5 OpportunityDetailPage
**Ruta:** `/dashboard/sales/opportunities/:id`
**Archivo:** `src/pages/sales/opportunities/[id].tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Detalle y gestión de una oportunidad |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | OpportunityForm, ActivityTimeline, ActivityForm |
| **Hooks** | useOpportunity, useDeleteOpportunity, useMarkAsWon, useMarkAsLost, useOpportunityActivities |
| **APIs** | Opportunities API, Activities API |
**Secciones:**
- Valor y probabilidad
- Descripción
- Timeline de actividades
- Status (Open/Won/Lost)
- Contacto
- Timeline de fechas
**Acciones:**
- Editar oportunidad
- Eliminar
- Marcar como Won
- Marcar como Lost
- Agregar actividad
---
### 3.6 ActivitiesPage
**Ruta:** `/dashboard/sales/activities`
**Archivo:** `src/pages/sales/activities/index.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Gestión de actividades de ventas |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | ActivityForm |
| **Hooks** | useActivities, useUpcomingActivities, useOverdueActivities, useActivityStats |
| **APIs** | Activities API |
**Tipos de actividad:**
- Call
- Meeting
- Task
- Email
- Note
**Estados:**
- Pending
- Completed
- Cancelled
---
## 4. Commissions Pages
### 4.1 CommissionsPage (Dashboard)
**Ruta:** `/dashboard/commissions`
**Archivo:** `src/pages/commissions/index.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Dashboard de comisiones |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | CommissionsDashboard |
| **Hooks** | useCommissionsSummary, useTopEarners, useEarningsByPeriod |
| **APIs** | Commissions Dashboard API |
**Métricas:**
- Total comisiones
- Pendientes
- Aprobadas
- Top earners
---
### 4.2 SchemesPage
**Ruta:** `/dashboard/commissions/schemes`
**Archivo:** `src/pages/commissions/schemes/index.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Configuración de esquemas de comisiones |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | SchemesList, SchemeForm |
| **Hooks** | useSchemes |
| **APIs** | Schemes API |
**Tipos de esquema:**
- Percentage
- Fixed
- Tiered
**Filtros:**
- Type
- Status (active/inactive)
- Search
---
### 4.3 EntriesPage
**Ruta:** `/dashboard/commissions/entries`
**Archivo:** `src/pages/commissions/entries/index.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Gestión de entradas de comisiones |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | EntriesList |
| **Hooks** | useEntries, useBulkApprove, useBulkReject |
| **APIs** | Entries API |
**Estados:**
- Pending
- Approved
- Rejected
- Paid
- Cancelled
**Acciones bulk:**
- Aprobar seleccionados
- Rechazar seleccionados
---
### 4.4 PeriodsPage
**Ruta:** `/dashboard/commissions/periods`
**Archivo:** `src/pages/commissions/periods/index.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Gestión de períodos de pago |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | PeriodManager |
| **Hooks** | usePeriods, useCreatePeriod |
| **APIs** | Periods API |
**Estados de período:**
- Open
- Closed
- Processing
- Paid
---
### 4.5 MyEarningsPage
**Ruta:** `/dashboard/commissions/my-earnings`
**Archivo:** `src/pages/commissions/my-earnings/index.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Vista de ganancias del usuario actual |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | EarningsCard, EntryStatusBadge |
| **Hooks** | useMyEarnings |
| **APIs** | Dashboard API (my earnings) |
**Secciones:**
- Total earnings
- Pending
- Approved
- Current period earnings
- Recent commissions table
---
## 5. Settings Pages
### 5.1 SettingsPage
**Ruta:** `/dashboard/settings`
**Archivo:** `src/pages/settings/index.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Configuración de cuenta (tabs) |
| **Layout** | DashboardLayout |
| **Guard** | ProtectedRoute |
| **Componentes** | GeneralSettings, NotificationSettings, SecuritySettings |
| **Hooks** | useAuth, useCurrentUser |
| **APIs** | `usersApi.update()`, `notificationsApi.*`, `authApi.*` |
**Tabs:**
- General (perfil)
- Notifications (preferencias)
- Security (password, MFA)
- AI (configuración AI)
---
### 5.2 GeneralSettings
**Archivo:** `src/pages/settings/GeneralSettings.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Configuración de perfil |
| **Componentes** | Form básico |
| **Hooks** | useCurrentUser, useForm |
**Campos:**
- First name
- Last name
- Email (read-only)
- Avatar
---
### 5.3 NotificationSettings
**Archivo:** `src/pages/settings/NotificationSettings.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Preferencias de notificaciones |
| **Componentes** | Toggle switches |
| **Hooks** | useNotifications |
**Configuración:**
- Email notifications
- Push notifications
- In-app notifications
- Tipos de eventos
---
### 5.4 SecuritySettings
**Archivo:** `src/pages/settings/SecuritySettings.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Seguridad de la cuenta |
| **Componentes** | Form de password |
| **Hooks** | useAuth |
**Secciones:**
- Cambiar contraseña
- Sesiones activas
- MFA (futuro)
---
## 6. Superadmin Pages
### 6.1 TenantsPage
**Ruta:** `/superadmin/tenants`
**Archivo:** `src/pages/superadmin/TenantsPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Listado de todos los tenants |
| **Layout** | DashboardLayout |
| **Guard** | SuperadminRoute |
| **Componentes** | TenantTable |
| **Hooks** | useSuperadmin |
| **APIs** | `superadminApi.listTenants()` |
**Filtros:**
- Search
- Status
- Plan
---
### 6.2 TenantDetailPage
**Ruta:** `/superadmin/tenants/:id`
**Archivo:** `src/pages/superadmin/TenantDetailPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Detalle de un tenant específico |
| **Layout** | DashboardLayout |
| **Guard** | SuperadminRoute |
| **Componentes** | TenantForm, UserTable |
| **Hooks** | useSuperadmin |
| **APIs** | `superadminApi.getTenant()`, `superadminApi.getTenantUsers()` |
**Secciones:**
- Información del tenant
- Usuarios del tenant
- Suscripción
- Métricas de uso
---
### 6.3 MetricsPage
**Ruta:** `/superadmin/metrics`
**Archivo:** `src/pages/superadmin/MetricsPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Métricas globales del sistema |
| **Layout** | DashboardLayout |
| **Guard** | SuperadminRoute |
| **Componentes** | MetricCard, TrendChart |
| **Hooks** | useSuperadmin |
| **APIs** | `superadminApi.getMetricsSummary()`, `superadminApi.getTenantGrowth()` |
**Métricas:**
- Total tenants
- Total users
- MRR
- Tenant growth chart
- Plan distribution
- Top tenants
---
## 7. Onboarding Pages
### 7.1 OnboardingPage
**Ruta:** `/onboarding`
**Archivo:** `src/pages/onboarding/OnboardingPage.tsx`
| Aspecto | Detalle |
|---------|---------|
| **Propósito** | Wizard de onboarding para nuevos tenants |
| **Layout** | Propio (sin DashboardLayout) |
| **Guard** | ProtectedRoute |
| **Componentes** | WizardProgress, CompanyStep, PlanStep, InviteStep, CompleteStep |
| **Hooks** | useOnboarding, useOnboardingStatus |
| **APIs** | Onboarding API |
**Pasos:**
1. **CompanyStep** - Información de la empresa (nombre, logo)
2. **PlanStep** - Selección de plan
3. **InviteStep** - Invitar miembros del equipo
4. **CompleteStep** - Confirmación y redirección
---
## Resumen de Estados Comunes
### Loading State
```tsx
<div className="flex items-center justify-center h-64">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600" />
</div>
```
### Empty State
```tsx
<div className="text-center py-12">
<p className="text-gray-500">No data found</p>
</div>
```
### Error State
```tsx
<div className="bg-red-50 p-4 rounded-lg">
<p className="text-red-600">{error.message}</p>
</div>
```
---
## Resumen Cuantitativo
| Categoría | Cantidad |
|-----------|----------|
| Auth Pages | 3 |
| Dashboard Core | 9 |
| Sales Pages | 6 |
| Commissions Pages | 5 |
| Settings Pages | 4 |
| Superadmin Pages | 3 |
| Onboarding Pages | 1 (+4 steps) |
| **Total** | **38** |
---
*Documentación generada - Template SaaS v1.0.0 - 2026-01-25*

View File

@ -0,0 +1,287 @@
# Frontend Routing - Template SaaS
**Versión:** 1.0.0
**Actualizado:** 2026-01-25
**Router:** React Router DOM v7.1.1
---
## Arquitectura de Routing
El sistema de routing está basado en **React Router v6/7** con guards de protección para controlar el acceso a las rutas según el estado de autenticación y rol del usuario.
### Archivo Principal
`apps/frontend/src/router/index.tsx`
---
## Guards de Protección
| Guard | Descripción | Redirección |
|-------|-------------|-------------|
| **GuestRoute** | Solo usuarios no autenticados | → `/dashboard` si está autenticado |
| **ProtectedRoute** | Requiere autenticación | → `/auth/login` si no está autenticado |
| **SuperadminRoute** | Requiere rol `superadmin` | → `/dashboard` si no tiene rol |
---
## Mapa de Rutas Completo
### 1. Rutas Públicas
| Ruta | Componente | Guard | Descripción |
|------|------------|-------|-------------|
| `/` | Navigate | - | Redirect a `/auth/login` |
### 2. Rutas de Autenticación (`/auth`)
**Layout:** `AuthLayout`
**Guard:** `GuestRoute`
| Ruta | Componente | Descripción |
|------|------------|-------------|
| `/auth/login` | `LoginPage` | Formulario de inicio de sesión |
| `/auth/register` | `RegisterPage` | Formulario de registro |
| `/auth/forgot-password` | `ForgotPasswordPage` | Recuperación de contraseña |
### 3. Rutas de Dashboard (`/dashboard`)
**Layout:** `DashboardLayout`
**Guard:** `ProtectedRoute`
#### 3.1 Rutas Core
| Ruta | Componente | Descripción |
|------|------------|-------------|
| `/dashboard` | `DashboardPage` | Dashboard principal con métricas |
| `/dashboard/settings` | `SettingsPage` | Configuración de cuenta |
| `/dashboard/billing` | `BillingPage` | Gestión de suscripción (Stripe) |
| `/dashboard/users` | `UsersPage` | Gestión de usuarios del tenant |
| `/dashboard/ai` | `AIPage` | Panel de control AI/LLM |
| `/dashboard/storage` | `StoragePage` | Gestión de archivos |
| `/dashboard/webhooks` | `WebhooksPage` | Configuración de webhooks |
| `/dashboard/audit` | `AuditLogsPage` | Registro de auditoría |
| `/dashboard/feature-flags` | `FeatureFlagsPage` | Feature toggles |
| `/dashboard/whatsapp` | `WhatsAppSettings` | Config WhatsApp Business |
#### 3.2 Rutas de Sales (`/dashboard/sales`)
| Ruta | Componente | Parámetros | Descripción |
|------|------------|------------|-------------|
| `/dashboard/sales` | `SalesPage` | - | Dashboard de ventas |
| `/dashboard/sales/leads` | `LeadsPage` | - | Listado de leads |
| `/dashboard/sales/leads/:id` | `LeadDetailPage` | `id: string` | Detalle de lead |
| `/dashboard/sales/opportunities` | `OpportunitiesPage` | - | Pipeline de oportunidades |
| `/dashboard/sales/opportunities/:id` | `OpportunityDetailPage` | `id: string` | Detalle de oportunidad |
| `/dashboard/sales/activities` | `ActivitiesPage` | - | Actividades de ventas |
#### 3.3 Rutas de Commissions (`/dashboard/commissions`)
| Ruta | Componente | Parámetros | Descripción |
|------|------------|------------|-------------|
| `/dashboard/commissions` | `CommissionsPage` | - | Dashboard de comisiones |
| `/dashboard/commissions/schemes` | `SchemesPage` | - | Esquemas de comisiones |
| `/dashboard/commissions/entries` | `EntriesPage` | - | Entradas de comisiones |
| `/dashboard/commissions/periods` | `PeriodsPage` | - | Períodos de pago |
| `/dashboard/commissions/my-earnings` | `MyEarningsPage` | - | Mis ganancias |
### 4. Rutas de Superadmin (`/superadmin`)
**Layout:** `DashboardLayout`
**Guard:** `SuperadminRoute`
| Ruta | Componente | Parámetros | Descripción |
|------|------------|------------|-------------|
| `/superadmin` | Navigate | - | Redirect a `/superadmin/tenants` |
| `/superadmin/tenants` | `TenantsPage` | - | Listado de tenants |
| `/superadmin/tenants/:id` | `TenantDetailPage` | `id: string` | Detalle de tenant |
| `/superadmin/metrics` | `MetricsPage` | - | Métricas del sistema |
### 5. Ruta de Onboarding
| Ruta | Componente | Guard | Descripción |
|------|------------|-------|-------------|
| `/onboarding` | `OnboardingPage` | `ProtectedRoute` | Wizard de onboarding (4 pasos) |
### 6. Ruta 404 (Catch-All)
| Ruta | Componente | Descripción |
|------|------------|-------------|
| `*` | Navigate | Redirect a `/` |
---
## Diagrama de Navegación
```
┌─────────────────┐
│ / │
│ (redirect) │
└────────┬────────┘
┌──────────────────┴──────────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ /auth/* │ │ /dashboard/* │
│ (GuestRoute) │ │ (ProtectedRoute)│
├─────────────────┤ ├─────────────────┤
│ login │◄─────────────────►│ (index) │
│ register │ login/logout │ settings │
│ forgot-password │ │ billing │
└─────────────────┘ │ users │
│ ai │
│ storage │
│ webhooks │
│ audit │
│ feature-flags │
│ whatsapp │
│ │
│ sales/* │
│ ├─ (index) │
│ ├─ leads │
│ ├─ leads/:id │
│ ├─ opportunities│
│ ├─ opportunities/:id │
│ └─ activities │
│ │
│ commissions/* │
│ ├─ (index) │
│ ├─ schemes │
│ ├─ entries │
│ ├─ periods │
│ └─ my-earnings │
└────────┬────────┘
│ (superadmin only)
┌─────────────────┐
│ /superadmin/* │
│(SuperadminRoute)│
├─────────────────┤
│ tenants │
│ tenants/:id │
│ metrics │
└─────────────────┘
```
---
## Layouts
### AuthLayout
- **Ubicación:** `src/layouts/AuthLayout.tsx`
- **Uso:** Rutas de autenticación (`/auth/*`)
- **Características:**
- Diseño de 2 columnas (branding + formulario)
- Responsive para móvil
- Sin sidebar ni navegación
### DashboardLayout
- **Ubicación:** `src/layouts/DashboardLayout.tsx`
- **Uso:** Dashboard y Superadmin
- **Características:**
- Sidebar fijo con navegación
- Top bar con usuario y notificaciones
- Contenido principal con scroll
- Menú dinámico según rol
- Outlet para rutas hijas
---
## Flujos de Navegación
### Flujo de Autenticación
```
Usuario no autenticado
├─► /dashboard → redirect → /auth/login
└─► /auth/login → login exitoso → /dashboard
```
### Flujo de Onboarding
```
Nuevo usuario (post-registro)
└─► /onboarding
├─ Step 1: CompanyStep (info empresa)
├─ Step 2: PlanStep (selección plan)
├─ Step 3: InviteStep (invitar usuarios)
└─ Step 4: CompleteStep → /dashboard
```
### Flujo de Superadmin
```
Usuario con role=superadmin
├─► /dashboard/* → acceso normal
└─► /superadmin/* → acceso permitido
└─ /superadmin/tenants/:id → detalle tenant
```
---
## Parámetros Dinámicos
| Ruta | Parámetro | Tipo | Descripción |
|------|-----------|------|-------------|
| `/dashboard/sales/leads/:id` | `id` | `string` (UUID) | ID del lead |
| `/dashboard/sales/opportunities/:id` | `id` | `string` (UUID) | ID de la oportunidad |
| `/superadmin/tenants/:id` | `id` | `string` (UUID) | ID del tenant |
### Uso en Componentes
```tsx
import { useParams } from 'react-router-dom';
function LeadDetailPage() {
const { id } = useParams<{ id: string }>();
// id contiene el UUID del lead
}
```
---
## Estado de Autenticación
### Store: `auth.store.ts`
```typescript
interface User {
id: string;
email: string;
first_name: string;
last_name: string;
role: string; // 'user' | 'admin' | 'superadmin'
tenant_id: string;
}
interface AuthState {
user: User | null;
accessToken: string | null;
refreshToken: string | null;
isAuthenticated: boolean;
isLoading: boolean;
}
```
---
## Resumen de Rutas
| Portal | Cantidad | Guard |
|--------|----------|-------|
| Auth | 3 rutas | GuestRoute |
| Dashboard Core | 10 rutas | ProtectedRoute |
| Dashboard Sales | 6 rutas | ProtectedRoute |
| Dashboard Commissions | 5 rutas | ProtectedRoute |
| Superadmin | 3 rutas | SuperadminRoute |
| Onboarding | 1 ruta | ProtectedRoute |
| **Total** | **28 rutas** | - |
---
*Documentación generada - Template SaaS v1.0.0*

View File

@ -1,16 +1,17 @@
---
# FRONTEND INVENTORY - Template SaaS
# Version: 4.0.0
# Ultima actualizacion: 2026-01-24
# Version: 4.1.0
# Ultima actualizacion: 2026-01-25
# Nota: AUDITORIA DE COHERENCIA - Sincronizado con codigo real
# CORRECCION 2026-01-24: Sales (SAAS-018) y Commissions (SAAS-020) SI implementados en frontend
# ACTUALIZACION 2026-01-25: Rutas Sales/Commissions agregadas al router, authStore completado
# Verificado: 6 paginas Sales, 5 paginas Commissions, APIs y hooks completos
metadata:
proyecto: "template-saas"
tipo: "FRONTEND"
version: "4.0.0"
updated: "2026-01-24"
version: "4.1.0"
updated: "2026-01-25"
framework: "React 19.0.0 + Vite 6.0.6"
styling: "Tailwind CSS 3.4.17"
state: "Zustand 5.0.2"
@ -219,18 +220,19 @@ shared:
implementados:
- nombre: "authStore"
archivo: "auth.store.ts"
estado: "parcial"
estado: "completado"
actions_implementadas:
- login
- logout
- setUser
- setTokens
- setLoading
actions_faltantes:
- refreshToken
- refreshTokens
- updateProfile
actions_faltantes: []
usa_persist: true
storage_key: "auth-storage"
nota: "COMPLETADO 2026-01-25: refreshTokens y updateProfile implementados"
- nombre: "uiStore"
archivo: "ui.store.ts"
estado: "completado"
@ -448,10 +450,13 @@ gaps_identificados:
- "4 stores Zustand adicionales pendientes"
medios:
- "Componentes Forms no implementados como wrappers"
- "authStore incompleto (falta refreshToken, updateProfile)"
resueltos_2026_01_24:
- "Portal Sales (SAAS-018): Ahora completado (6 paginas)"
- "Portal Commissions (SAAS-020): Ahora completado (5 paginas)"
resueltos_2026_01_25:
- "authStore completado (refreshTokens y updateProfile implementados)"
- "Rutas Sales/Commissions agregadas al router (antes existian paginas pero no rutas)"
- "Documentacion FRONTEND-ROUTING.md creada"
dependencias_npm:
core:
@ -477,9 +482,13 @@ dependencias_npm:
notifications:
- "socket.io-client"
ultima_actualizacion: "2026-01-24"
actualizado_por: "Claude Opus 4.5 (Auditoria de Coherencia)"
ultima_actualizacion: "2026-01-25"
actualizado_por: "Claude Opus 4.5 (Alineacion Doc-Codigo)"
historial_cambios:
- fecha: "2026-01-25"
tipo: "alineacion"
descripcion: "Rutas Sales/Commissions agregadas al router. authStore completado (refreshTokens, updateProfile). Documentacion FRONTEND-ROUTING.md creada."
agente: "Claude Opus 4.5 (Alineacion Doc-Codigo)"
- fecha: "2026-01-24"
tipo: "correccion_critica"
descripcion: "CORRECCION: Sales (6 paginas) y Commissions (5 paginas) SI implementados. Build de frontend exitoso (2733 modulos). APIs y hooks ya listados correctamente."