diff --git a/apps/frontend/src/components/ai/AIChat.tsx b/apps/frontend/src/components/ai/AIChat.tsx index d4f73a2e..4e5179ef 100644 --- a/apps/frontend/src/components/ai/AIChat.tsx +++ b/apps/frontend/src/components/ai/AIChat.tsx @@ -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 + * + * + * @example + * // With custom system prompt and model selector + * refetchUsage()} + * className="h-[600px]" + * /> + */ export function AIChat({ systemPrompt, placeholder = 'Type your message...', diff --git a/apps/frontend/src/components/ai/AISettings.tsx b/apps/frontend/src/components/ai/AISettings.tsx index c0e987a0..31e1100e 100644 --- a/apps/frontend/src/components/ai/AISettings.tsx +++ b/apps/frontend/src/components/ai/AISettings.tsx @@ -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 + * + * + * @example + * // Typically used within an admin or settings layout + * + * + * + */ export function AISettings() { const { data: config, isLoading: configLoading } = useAIConfig(); const { data: models } = useAIModels(); diff --git a/apps/frontend/src/components/ai/ChatMessage.tsx b/apps/frontend/src/components/ai/ChatMessage.tsx index 3102bb40..fe623e02 100644 --- a/apps/frontend/src/components/ai/ChatMessage.tsx +++ b/apps/frontend/src/components/ai/ChatMessage.tsx @@ -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 + * + * + * @example + * // Assistant message with loading state + * + * + * @example + * // System message + * + */ export function ChatMessage({ role, content, timestamp, isLoading }: ChatMessageProps) { const isUser = role === 'user'; const isSystem = role === 'system'; diff --git a/apps/frontend/src/components/audit/ActivityTimeline.tsx b/apps/frontend/src/components/audit/ActivityTimeline.tsx index 6beb0d51..66082c96 100644 --- a/apps/frontend/src/components/audit/ActivityTimeline.tsx +++ b/apps/frontend/src/components/audit/ActivityTimeline.tsx @@ -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 = { 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 + * + * ``` + */ export function ActivityTimeline({ activities, isLoading }: ActivityTimelineProps) { if (isLoading) { return ( diff --git a/apps/frontend/src/components/audit/AuditFilters.tsx b/apps/frontend/src/components/audit/AuditFilters.tsx index 97bed0dc..e8965d00 100644 --- a/apps/frontend/src/components/audit/AuditFilters.tsx +++ b/apps/frontend/src/components/audit/AuditFilters.tsx @@ -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 + * + * ``` + */ 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 ( diff --git a/apps/frontend/src/components/audit/AuditLogRow.tsx b/apps/frontend/src/components/audit/AuditLogRow.tsx index 5cede424..ba17e5d5 100644 --- a/apps/frontend/src/components/audit/AuditLogRow.tsx +++ b/apps/frontend/src/components/audit/AuditLogRow.tsx @@ -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 + * openDetailModal(log)} + * /> + * ``` + */ export function AuditLogRow({ log, onSelect }: AuditLogRowProps) { const [expanded, setExpanded] = useState(false); diff --git a/apps/frontend/src/components/audit/AuditStatsCard.tsx b/apps/frontend/src/components/audit/AuditStatsCard.tsx index ae273e2a..93f835d4 100644 --- a/apps/frontend/src/components/audit/AuditStatsCard.tsx +++ b/apps/frontend/src/components/audit/AuditStatsCard.tsx @@ -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 + * + * ``` + */ export function AuditStatsCard({ stats, isLoading }: AuditStatsCardProps) { if (isLoading) { return ( diff --git a/apps/frontend/src/components/commissions/CommissionsDashboard.tsx b/apps/frontend/src/components/commissions/CommissionsDashboard.tsx index acee82df..b7ce53d7 100644 --- a/apps/frontend/src/components/commissions/CommissionsDashboard.tsx +++ b/apps/frontend/src/components/commissions/CommissionsDashboard.tsx @@ -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 + * + * ``` + */ export function CommissionsDashboard({ summary, topEarners, periodEarnings }: CommissionsDashboardProps) { const formatCurrency = (amount: number) => new Intl.NumberFormat('en-US', { style: 'currency', currency: summary.currency }).format(amount); diff --git a/apps/frontend/src/components/commissions/EarningsCard.tsx b/apps/frontend/src/components/commissions/EarningsCard.tsx index aacff2ad..fe486fb9 100644 --- a/apps/frontend/src/components/commissions/EarningsCard.tsx +++ b/apps/frontend/src/components/commissions/EarningsCard.tsx @@ -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'; + * + * } + * color="green" + * subtitle="Last 30 days" + * /> + * ``` + */ export function EarningsCard({ title, value, icon, color, subtitle }: EarningsCardProps) { const colorClasses = { blue: 'bg-blue-100 text-blue-600', diff --git a/apps/frontend/src/components/commissions/EntriesList.tsx b/apps/frontend/src/components/commissions/EntriesList.tsx index a2dc479b..f4c5fa4e 100644 --- a/apps/frontend/src/components/commissions/EntriesList.tsx +++ b/apps/frontend/src/components/commissions/EntriesList.tsx @@ -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([]); + * const [page, setPage] = useState(1); + * + * refetch()} + * /> + * ``` + */ export function EntriesList({ entries, total, diff --git a/apps/frontend/src/components/commissions/EntryStatusBadge.tsx b/apps/frontend/src/components/commissions/EntryStatusBadge.tsx index 3db198b7..4a4dde23 100644 --- a/apps/frontend/src/components/commissions/EntryStatusBadge.tsx +++ b/apps/frontend/src/components/commissions/EntryStatusBadge.tsx @@ -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 + * + * + * + * ``` + */ export function EntryStatusBadge({ status }: EntryStatusBadgeProps) { const getStatusConfig = (status: EntryStatus) => { switch (status) { diff --git a/apps/frontend/src/components/commissions/PeriodManager.tsx b/apps/frontend/src/components/commissions/PeriodManager.tsx index c43f99d4..1b917fcf 100644 --- a/apps/frontend/src/components/commissions/PeriodManager.tsx +++ b/apps/frontend/src/components/commissions/PeriodManager.tsx @@ -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); + * + * refetch()} + * /> + * ``` + */ export function PeriodManager({ periods, total, diff --git a/apps/frontend/src/components/commissions/SchemeForm.tsx b/apps/frontend/src/components/commissions/SchemeForm.tsx index e8504744..03ea41c2 100644 --- a/apps/frontend/src/components/commissions/SchemeForm.tsx +++ b/apps/frontend/src/components/commissions/SchemeForm.tsx @@ -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 + * setShowForm(false)} + * onSuccess={() => { + * setShowForm(false); + * refetch(); + * }} + * /> + * + * // Edit mode + * setShowForm(false)} + * onSuccess={() => { + * setShowForm(false); + * refetch(); + * }} + * /> + * ``` + */ export function SchemeForm({ scheme, onClose, onSuccess }: SchemeFormProps) { const isEditing = !!scheme; const createMutation = useCreateScheme(); diff --git a/apps/frontend/src/components/commissions/SchemesList.tsx b/apps/frontend/src/components/commissions/SchemesList.tsx index 2e309894..dda46bab 100644 --- a/apps/frontend/src/components/commissions/SchemesList.tsx +++ b/apps/frontend/src/components/commissions/SchemesList.tsx @@ -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); + * + * refetch()} + * /> + * ``` + */ export function SchemesList({ schemes, total, diff --git a/apps/frontend/src/components/feature-flags/FeatureFlagCard.tsx b/apps/frontend/src/components/feature-flags/FeatureFlagCard.tsx index ccb40d60..6a3af57f 100644 --- a/apps/frontend/src/components/feature-flags/FeatureFlagCard.tsx +++ b/apps/frontend/src/components/feature-flags/FeatureFlagCard.tsx @@ -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 + * console.log('Edit', flag)} + * onDelete={(flag) => console.log('Delete', flag)} + * onToggle={(flag) => console.log('Toggle', flag)} + * isToggling={false} + * /> + * ``` + */ export function FeatureFlagCard({ flag, onEdit, diff --git a/apps/frontend/src/components/feature-flags/FeatureFlagForm.tsx b/apps/frontend/src/components/feature-flags/FeatureFlagForm.tsx index 1e978429..d2d0f9c7 100644 --- a/apps/frontend/src/components/feature-flags/FeatureFlagForm.tsx +++ b/apps/frontend/src/components/feature-flags/FeatureFlagForm.tsx @@ -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 + * createFlag(data)} + * onCancel={() => closeModal()} + * isLoading={isCreating} + * /> + * + * // Edit mode + * updateFlag(existingFlag.id, data)} + * onCancel={() => closeModal()} + * isLoading={isUpdating} + * /> + * ``` + */ export function FeatureFlagForm({ flag, onSubmit, onCancel, isLoading }: FeatureFlagFormProps) { const isEditing = !!flag; diff --git a/apps/frontend/src/components/feature-flags/TenantOverridesPanel.tsx b/apps/frontend/src/components/feature-flags/TenantOverridesPanel.tsx index a0ecce33..21bd7ee9 100644 --- a/apps/frontend/src/components/feature-flags/TenantOverridesPanel.tsx +++ b/apps/frontend/src/components/feature-flags/TenantOverridesPanel.tsx @@ -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 + * console.log('Add override', flagId, isEnabled)} + * onRemove={(flagId) => console.log('Remove override', flagId)} + * isLoading={false} + * /> + * ``` + */ export function TenantOverridesPanel({ flags, overrides, diff --git a/apps/frontend/src/components/notifications/DevicesManager.tsx b/apps/frontend/src/components/notifications/DevicesManager.tsx index 9bee277c..393dfe3c 100644 --- a/apps/frontend/src/components/notifications/DevicesManager.tsx +++ b/apps/frontend/src/components/notifications/DevicesManager.tsx @@ -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 + * + * + * @example + * // Without header (for embedding in settings pages) + * + */ export function DevicesManager({ showHeader = true }: DevicesManagerProps) { const { data: devices, isLoading, error, refetch } = useDevices(); const { unregisterDevice, isSubscribed, requestPermission, isLoading: pushLoading } = usePushNotifications(); diff --git a/apps/frontend/src/components/notifications/NotificationBell.tsx b/apps/frontend/src/components/notifications/NotificationBell.tsx index 5f22d425..6a0379f3 100644 --- a/apps/frontend/src/components/notifications/NotificationBell.tsx +++ b/apps/frontend/src/components/notifications/NotificationBell.tsx @@ -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 + *
+ * + *
+ * + * @example + * // Usage in a toolbar + *
+ * + * + *
+ */ export function NotificationBell() { const [isDrawerOpen, setIsDrawerOpen] = useState(false); const { data } = useUnreadNotificationsCount(); diff --git a/apps/frontend/src/components/notifications/NotificationDrawer.tsx b/apps/frontend/src/components/notifications/NotificationDrawer.tsx index 86f3c213..04ed9ed0 100644 --- a/apps/frontend/src/components/notifications/NotificationDrawer.tsx +++ b/apps/frontend/src/components/notifications/NotificationDrawer.tsx @@ -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); + * + * + * setIsOpen(false)} + * /> + */ export function NotificationDrawer({ isOpen, onClose }: NotificationDrawerProps) { const navigate = useNavigate(); const { data, isLoading } = useNotifications(1, 20); diff --git a/apps/frontend/src/components/notifications/NotificationItem.tsx b/apps/frontend/src/components/notifications/NotificationItem.tsx index da822f08..67ed7d05 100644 --- a/apps/frontend/src/components/notifications/NotificationItem.tsx +++ b/apps/frontend/src/components/notifications/NotificationItem.tsx @@ -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 = { 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 + * 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; diff --git a/apps/frontend/src/components/notifications/PushPermissionBanner.tsx b/apps/frontend/src/components/notifications/PushPermissionBanner.tsx index 88e40a59..16864b9e 100644 --- a/apps/frontend/src/components/notifications/PushPermissionBanner.tsx +++ b/apps/frontend/src/components/notifications/PushPermissionBanner.tsx @@ -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 + *
+ * + * + *
+ * + * @example + * // With custom dismiss handling + * { + * analytics.track('push_banner_dismissed'); + * }} + * /> + */ export function PushPermissionBanner({ onDismiss }: PushPermissionBannerProps) { const { isSupported, diff --git a/apps/frontend/src/components/sales/ActivityForm.tsx b/apps/frontend/src/components/sales/ActivityForm.tsx index e6a26589..8e5b8538 100644 --- a/apps/frontend/src/components/sales/ActivityForm.tsx +++ b/apps/frontend/src/components/sales/ActivityForm.tsx @@ -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 + * setShowForm(false)} + * /> + * + * // Activity for an opportunity + * setShowForm(false)} + * /> + * ``` + */ export function ActivityForm({ leadId, opportunityId, onClose }: ActivityFormProps) { const [formData, setFormData] = useState({ type: 'task', diff --git a/apps/frontend/src/components/sales/ActivityTimeline.tsx b/apps/frontend/src/components/sales/ActivityTimeline.tsx index e547c91e..47983048 100644 --- a/apps/frontend/src/components/sales/ActivityTimeline.tsx +++ b/apps/frontend/src/components/sales/ActivityTimeline.tsx @@ -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 + * + * ``` + */ export function ActivityTimeline({ activities }: ActivityTimelineProps) { const getIcon = (type: ActivityType) => { switch (type) { diff --git a/apps/frontend/src/components/sales/ConversionFunnel.tsx b/apps/frontend/src/components/sales/ConversionFunnel.tsx index a64dbf42..09ff8efa 100644 --- a/apps/frontend/src/components/sales/ConversionFunnel.tsx +++ b/apps/frontend/src/components/sales/ConversionFunnel.tsx @@ -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 + * + * ``` + */ export function ConversionFunnel({ conversion }: ConversionFunnelProps) { const funnelStages = [ { name: 'Leads', value: 100, color: 'bg-blue-500' }, diff --git a/apps/frontend/src/components/sales/LeadCard.tsx b/apps/frontend/src/components/sales/LeadCard.tsx index f665afec..21530017 100644 --- a/apps/frontend/src/components/sales/LeadCard.tsx +++ b/apps/frontend/src/components/sales/LeadCard.tsx @@ -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 + * + * ``` + */ export function LeadCard({ lead }: LeadCardProps) { const getStatusColor = (status: string) => { switch (status) { diff --git a/apps/frontend/src/components/sales/LeadForm.tsx b/apps/frontend/src/components/sales/LeadForm.tsx index e3cf1ae0..b3dd5d7d 100644 --- a/apps/frontend/src/components/sales/LeadForm.tsx +++ b/apps/frontend/src/components/sales/LeadForm.tsx @@ -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 + * setShowForm(false)} /> + * + * // Edit mode + * setShowForm(false)} + * /> + * ``` + */ export function LeadForm({ lead, onClose }: LeadFormProps) { const [formData, setFormData] = useState({ first_name: lead?.first_name || '', diff --git a/apps/frontend/src/components/sales/LeadsList.tsx b/apps/frontend/src/components/sales/LeadsList.tsx index 4e60024d..550a3c26 100644 --- a/apps/frontend/src/components/sales/LeadsList.tsx +++ b/apps/frontend/src/components/sales/LeadsList.tsx @@ -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 + * setCurrentPage(newPage)} + * /> + * ``` + */ export function LeadsList({ leads, total, page, totalPages, onPageChange }: LeadsListProps) { const navigate = useNavigate(); diff --git a/apps/frontend/src/components/sales/OpportunityCard.tsx b/apps/frontend/src/components/sales/OpportunityCard.tsx index 0b1389ee..aa529a3a 100644 --- a/apps/frontend/src/components/sales/OpportunityCard.tsx +++ b/apps/frontend/src/components/sales/OpportunityCard.tsx @@ -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 + * navigate(`/opportunities/${opportunityData.id}`)} + * /> + * + * // Compact mode for pipeline boards + * handleSelect(opportunityData)} + * /> + * ``` + */ export function OpportunityCard({ opportunity, compact = false, onClick }: OpportunityCardProps) { const formatCurrency = (amount: number) => { return new Intl.NumberFormat('en-US', { diff --git a/apps/frontend/src/components/sales/OpportunityForm.tsx b/apps/frontend/src/components/sales/OpportunityForm.tsx index 588925c0..97875b69 100644 --- a/apps/frontend/src/components/sales/OpportunityForm.tsx +++ b/apps/frontend/src/components/sales/OpportunityForm.tsx @@ -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 + * setShowForm(false)} /> + * + * // Edit mode + * setShowForm(false)} + * /> + * ``` + */ export function OpportunityForm({ opportunity, onClose }: OpportunityFormProps) { const [formData, setFormData] = useState({ name: opportunity?.name || '', diff --git a/apps/frontend/src/components/sales/PipelineBoard.tsx b/apps/frontend/src/components/sales/PipelineBoard.tsx index 8a479df4..89cc63f8 100644 --- a/apps/frontend/src/components/sales/PipelineBoard.tsx +++ b/apps/frontend/src/components/sales/PipelineBoard.tsx @@ -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 + * + * ``` + */ export function PipelineBoard({ stages }: PipelineBoardProps) { const navigate = useNavigate(); diff --git a/apps/frontend/src/components/sales/SalesDashboard.tsx b/apps/frontend/src/components/sales/SalesDashboard.tsx index d614a8c4..319e41b9 100644 --- a/apps/frontend/src/components/sales/SalesDashboard.tsx +++ b/apps/frontend/src/components/sales/SalesDashboard.tsx @@ -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 + * + * ``` + */ export function SalesDashboard({ summary, conversion }: SalesDashboardProps) { const formatCurrency = (amount: number) => { return new Intl.NumberFormat('en-US', { diff --git a/apps/frontend/src/components/storage/FileItem.tsx b/apps/frontend/src/components/storage/FileItem.tsx index 33af2244..c5f6ee50 100644 --- a/apps/frontend/src/components/storage/FileItem.tsx +++ b/apps/frontend/src/components/storage/FileItem.tsx @@ -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 + * + * + * @example + * // Grid view with preview handler + * openPreviewModal(file)} + * onDelete={() => refetchFiles()} + * /> + */ export function FileItem({ file, view = 'list', onDelete, onPreview }: FileItemProps) { const [showMenu, setShowMenu] = useState(false); const [downloading, setDownloading] = useState(false); diff --git a/apps/frontend/src/components/storage/FileList.tsx b/apps/frontend/src/components/storage/FileList.tsx index 9d9bee2f..c150f8fd 100644 --- a/apps/frontend/src/components/storage/FileList.tsx +++ b/apps/frontend/src/components/storage/FileList.tsx @@ -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 + * + * + * @example + * // With folder filter and preview handler + * setPreviewFile(file)} + * className="mt-4" + * /> + */ export function FileList({ folder, onPreview, className = '' }: FileListProps) { const [view, setView] = useState<'grid' | 'list'>('list'); const [page, setPage] = useState(1); diff --git a/apps/frontend/src/components/storage/FileUpload.tsx b/apps/frontend/src/components/storage/FileUpload.tsx index 3f2e66e2..eba6c2d6 100644 --- a/apps/frontend/src/components/storage/FileUpload.tsx +++ b/apps/frontend/src/components/storage/FileUpload.tsx @@ -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 + * console.log('Uploaded:', file.id)} /> + * + * @example + * // With custom settings + * toast.error(err.message)} + * /> + */ export function FileUpload({ folder = 'files', visibility = 'private', diff --git a/apps/frontend/src/components/storage/StorageUsageCard.tsx b/apps/frontend/src/components/storage/StorageUsageCard.tsx index 1fa330d7..7cb3efc9 100644 --- a/apps/frontend/src/components/storage/StorageUsageCard.tsx +++ b/apps/frontend/src/components/storage/StorageUsageCard.tsx @@ -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 + * + * + * @example + * // With custom styling + * + */ export function StorageUsageCard({ className = '' }: StorageUsageCardProps) { const { data, isLoading, error } = useStorageUsage(); diff --git a/apps/frontend/src/components/webhooks/WebhookCard.tsx b/apps/frontend/src/components/webhooks/WebhookCard.tsx index 266e83bd..a5d01806 100644 --- a/apps/frontend/src/components/webhooks/WebhookCard.tsx +++ b/apps/frontend/src/components/webhooks/WebhookCard.tsx @@ -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 + * openEditModal(wh)} + * onDelete={(wh) => confirmDelete(wh)} + * onTest={(wh) => sendTestWebhook(wh)} + * onToggle={(wh) => toggleWebhookStatus(wh)} + * onViewDeliveries={(wh) => showDeliveryHistory(wh)} + * /> + * ``` + */ export function WebhookCard({ webhook, onEdit, diff --git a/apps/frontend/src/components/webhooks/WebhookDeliveryList.tsx b/apps/frontend/src/components/webhooks/WebhookDeliveryList.tsx index ac015c7d..1124a1f3 100644 --- a/apps/frontend/src/components/webhooks/WebhookDeliveryList.tsx +++ b/apps/frontend/src/components/webhooks/WebhookDeliveryList.tsx @@ -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 + * retryDelivery(deliveryId)} + * onLoadMore={() => fetchMoreDeliveries()} + * hasMore={hasMoreDeliveries} + * /> + * ``` + */ export function WebhookDeliveryList({ deliveries, isLoading, diff --git a/apps/frontend/src/components/webhooks/WebhookForm.tsx b/apps/frontend/src/components/webhooks/WebhookForm.tsx index ef8f199e..4932938a 100644 --- a/apps/frontend/src/components/webhooks/WebhookForm.tsx +++ b/apps/frontend/src/components/webhooks/WebhookForm.tsx @@ -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 + * createWebhook(data)} + * onCancel={() => closeModal()} + * /> + * + * // Editing an existing webhook + * updateWebhook(existingWebhook.id, data)} + * onCancel={() => closeModal()} + * isLoading={isSaving} + * /> + * ``` + */ export function WebhookForm({ webhook, events, diff --git a/apps/frontend/src/components/whatsapp/WhatsAppTestMessage.tsx b/apps/frontend/src/components/whatsapp/WhatsAppTestMessage.tsx index 239caa26..f5a8d10c 100644 --- a/apps/frontend/src/components/whatsapp/WhatsAppTestMessage.tsx +++ b/apps/frontend/src/components/whatsapp/WhatsAppTestMessage.tsx @@ -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 + * + * + * @example + * // Disabled state when integration is not configured + * + * + * @example + * // Usage within a settings panel + *
+ * + * + *
+ */ export function WhatsAppTestMessage({ disabled }: WhatsAppTestMessageProps) { const [phoneNumber, setPhoneNumber] = useState(''); const testConnection = useTestWhatsAppConnection(); diff --git a/apps/frontend/src/router/index.tsx b/apps/frontend/src/router/index.tsx index c62e8c67..67a9109c 100644 --- a/apps/frontend/src/router/index.tsx +++ b/apps/frontend/src/router/index.tsx @@ -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() { } /> } /> } /> + + {/* Sales routes */} + } /> + } /> + } /> + } /> + } /> + } /> + + {/* Commissions routes */} + } /> + } /> + } /> + } /> + } /> {/* Superadmin routes */} diff --git a/apps/frontend/src/stores/auth.store.ts b/apps/frontend/src/stores/auth.store.ts index eb5d73ff..ba7af1a5 100644 --- a/apps/frontend/src/stores/auth.store.ts +++ b/apps/frontend/src/stores/auth.store.ts @@ -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; + updateProfile: (data: UpdateProfileData) => void; } export const useAuthStore = create()( @@ -57,6 +64,57 @@ export const useAuthStore = create()( }), 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', diff --git a/docs/05-frontend/FRONTEND-PAGES-SPEC.md b/docs/05-frontend/FRONTEND-PAGES-SPEC.md new file mode 100644 index 00000000..d107c67f --- /dev/null +++ b/docs/05-frontend/FRONTEND-PAGES-SPEC.md @@ -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 +
+
+
+``` + +### Empty State +```tsx +
+

No data found

+
+``` + +### Error State +```tsx +
+

{error.message}

+
+``` + +--- + +## 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* diff --git a/docs/05-frontend/FRONTEND-ROUTING.md b/docs/05-frontend/FRONTEND-ROUTING.md new file mode 100644 index 00000000..4868088b --- /dev/null +++ b/docs/05-frontend/FRONTEND-ROUTING.md @@ -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* diff --git a/orchestration/inventarios/FRONTEND_INVENTORY.yml b/orchestration/inventarios/FRONTEND_INVENTORY.yml index d25ba8c2..91ca392c 100644 --- a/orchestration/inventarios/FRONTEND_INVENTORY.yml +++ b/orchestration/inventarios/FRONTEND_INVENTORY.yml @@ -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."