feat(frontend): Align documentation with development - complete frontend audit
- Add Sales/Commissions routes to router (11 new routes) - Complete authStore (refreshTokens, updateProfile methods) - Add JSDoc documentation to 40+ components across all categories - Create FRONTEND-ROUTING.md with complete route mapping - Create FRONTEND-PAGES-SPEC.md with 38 page specifications - Update FRONTEND_INVENTORY.yml to v4.1.0 with resolved gaps Components documented: ai/, audit/, commissions/, feature-flags/, notifications/, sales/, storage/, webhooks/, whatsapp/ Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0b90d87c1f
commit
f853c49568
@ -17,6 +17,32 @@ interface AIChatProps {
|
||||
onUsageUpdate?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* AI Chat component that provides an interactive chat interface with an AI assistant.
|
||||
* Supports conversation history, model selection, and configurable system prompts.
|
||||
*
|
||||
* @description Interactive chat component for communicating with AI models via OpenRouter.
|
||||
* Features auto-scrolling, message history, loading states, and customizable appearance.
|
||||
*
|
||||
* @param {string} [systemPrompt] - Custom system prompt to override the default AI behavior
|
||||
* @param {string} [placeholder='Type your message...'] - Placeholder text for the input field
|
||||
* @param {string} [className] - Additional CSS classes to apply to the container
|
||||
* @param {boolean} [showModelSelector=false] - Whether to show the AI model selection dropdown
|
||||
* @param {() => void} [onUsageUpdate] - Callback function triggered after each AI response for usage tracking
|
||||
*
|
||||
* @example
|
||||
* // Basic usage
|
||||
* <AIChat />
|
||||
*
|
||||
* @example
|
||||
* // With custom system prompt and model selector
|
||||
* <AIChat
|
||||
* systemPrompt="You are a helpful coding assistant."
|
||||
* showModelSelector={true}
|
||||
* onUsageUpdate={() => refetchUsage()}
|
||||
* className="h-[600px]"
|
||||
* />
|
||||
*/
|
||||
export function AIChat({
|
||||
systemPrompt,
|
||||
placeholder = 'Type your message...',
|
||||
|
||||
@ -13,6 +13,25 @@ interface AIConfigForm {
|
||||
log_conversations: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* AI Settings component for configuring AI integration at the organization level.
|
||||
* Provides controls for enabling/disabling AI, selecting models, adjusting parameters,
|
||||
* and viewing usage statistics.
|
||||
*
|
||||
* @description Administrative panel for managing AI configuration including model selection,
|
||||
* temperature, max tokens, system prompts, and privacy settings. Displays real-time health
|
||||
* status and usage metrics.
|
||||
*
|
||||
* @example
|
||||
* // Basic usage in settings page
|
||||
* <AISettings />
|
||||
*
|
||||
* @example
|
||||
* // Typically used within an admin or settings layout
|
||||
* <SettingsLayout>
|
||||
* <AISettings />
|
||||
* </SettingsLayout>
|
||||
*/
|
||||
export function AISettings() {
|
||||
const { data: config, isLoading: configLoading } = useAIConfig();
|
||||
const { data: models } = useAIModels();
|
||||
|
||||
@ -8,6 +8,42 @@ export interface ChatMessageProps {
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chat message component that renders a single message in the AI chat interface.
|
||||
* Supports different visual styles for user, assistant, and system messages.
|
||||
*
|
||||
* @description Displays a chat message with role-specific styling, avatar icons,
|
||||
* and optional timestamp. Handles loading states with animated dots for pending
|
||||
* assistant responses. System messages are rendered as centered badges.
|
||||
*
|
||||
* @param {'user' | 'assistant' | 'system'} role - The role of the message sender
|
||||
* @param {string} content - The text content of the message
|
||||
* @param {Date} [timestamp] - Optional timestamp to display below the message
|
||||
* @param {boolean} [isLoading=false] - Whether to show loading animation instead of content
|
||||
*
|
||||
* @example
|
||||
* // User message
|
||||
* <ChatMessage
|
||||
* role="user"
|
||||
* content="Hello, can you help me?"
|
||||
* timestamp={new Date()}
|
||||
* />
|
||||
*
|
||||
* @example
|
||||
* // Assistant message with loading state
|
||||
* <ChatMessage
|
||||
* role="assistant"
|
||||
* content=""
|
||||
* isLoading={true}
|
||||
* />
|
||||
*
|
||||
* @example
|
||||
* // System message
|
||||
* <ChatMessage
|
||||
* role="system"
|
||||
* content="Conversation started"
|
||||
* />
|
||||
*/
|
||||
export function ChatMessage({ role, content, timestamp, isLoading }: ChatMessageProps) {
|
||||
const isUser = role === 'user';
|
||||
const isSystem = role === 'system';
|
||||
|
||||
@ -15,8 +15,13 @@ import {
|
||||
} from 'lucide-react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/**
|
||||
* Props for the ActivityTimeline component.
|
||||
*/
|
||||
interface ActivityTimelineProps {
|
||||
/** Array of activity logs to display in the timeline */
|
||||
activities: ActivityLog[];
|
||||
/** Whether the component is in a loading state */
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
@ -46,6 +51,26 @@ const colorMap: Record<string, string> = {
|
||||
payment: 'bg-emerald-100 text-emerald-600 dark:bg-emerald-900/30 dark:text-emerald-400',
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays a vertical timeline of user activity events.
|
||||
*
|
||||
* @description Renders a chronological list of activity logs with type-specific icons
|
||||
* and color coding. Each activity shows the type label, description, user name,
|
||||
* resource type, relative timestamp, and any associated metadata. Supports loading
|
||||
* state with animated skeleton placeholders.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.activities - Array of activity logs to display in the timeline
|
||||
* @param props.isLoading - Whether to show loading skeleton instead of data
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <ActivityTimeline
|
||||
* activities={recentActivities}
|
||||
* isLoading={isLoadingActivities}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function ActivityTimeline({ activities, isLoading }: ActivityTimelineProps) {
|
||||
if (isLoading) {
|
||||
return (
|
||||
|
||||
@ -4,9 +4,15 @@ import { getAuditActionLabel } from '@/hooks/useAudit';
|
||||
import { Filter, X, Search } from 'lucide-react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/**
|
||||
* Props for the AuditFilters component.
|
||||
*/
|
||||
interface AuditFiltersProps {
|
||||
/** Current filter parameters applied to the audit log query */
|
||||
filters: QueryAuditLogsParams;
|
||||
/** Callback invoked when any filter value changes */
|
||||
onFiltersChange: (filters: QueryAuditLogsParams) => void;
|
||||
/** List of available entity types for the entity type filter dropdown */
|
||||
entityTypes?: string[];
|
||||
}
|
||||
|
||||
@ -21,6 +27,28 @@ const AUDIT_ACTIONS: AuditAction[] = [
|
||||
'import',
|
||||
];
|
||||
|
||||
/**
|
||||
* Provides a collapsible filter panel for audit log queries.
|
||||
*
|
||||
* @description Renders a filter interface with controls for action type, entity type,
|
||||
* and date range (from/to). Supports expanding/collapsing the filter panel, displays
|
||||
* active filter count as a badge, and shows removable filter badges when collapsed.
|
||||
* Automatically resets pagination to page 1 when filters change.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.filters - Current filter parameters applied to the audit log query
|
||||
* @param props.onFiltersChange - Callback invoked when any filter value changes
|
||||
* @param props.entityTypes - List of available entity types for the dropdown
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <AuditFilters
|
||||
* filters={queryParams}
|
||||
* onFiltersChange={setQueryParams}
|
||||
* entityTypes={['user', 'tenant', 'subscription']}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function AuditFilters({ filters, onFiltersChange, entityTypes = [] }: AuditFiltersProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
@ -181,6 +209,16 @@ export function AuditFilters({ filters, onFiltersChange, entityTypes = [] }: Aud
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a removable badge for an active filter.
|
||||
*
|
||||
* @description Internal component that renders a pill-shaped badge showing
|
||||
* the filter label with a close button to remove the filter.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.label - Text to display in the badge
|
||||
* @param props.onRemove - Callback invoked when the remove button is clicked
|
||||
*/
|
||||
function FilterBadge({ label, onRemove }: { label: string; onRemove: () => void }) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-1 bg-primary-100 dark:bg-primary-900/30 text-primary-700 dark:text-primary-400 rounded-full text-sm">
|
||||
|
||||
@ -4,11 +4,35 @@ import { getAuditActionLabel, getAuditActionColor } from '@/hooks/useAudit';
|
||||
import { ChevronDown, ChevronRight, User, Clock, Globe, Code } from 'lucide-react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/**
|
||||
* Props for the AuditLogRow component.
|
||||
*/
|
||||
interface AuditLogRowProps {
|
||||
/** The audit log entry to display */
|
||||
log: AuditLog;
|
||||
/** Optional callback invoked when the user clicks to view full details */
|
||||
onSelect?: (log: AuditLog) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a single audit log entry as an expandable row.
|
||||
*
|
||||
* @description Renders an audit log with summary information (action, entity, user, timestamp)
|
||||
* in a collapsed state. When expanded, shows detailed information including old/new values,
|
||||
* changed fields, metadata, IP address, endpoint, and response status.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.log - The audit log entry to display
|
||||
* @param props.onSelect - Optional callback invoked when the user clicks to view full details
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <AuditLogRow
|
||||
* log={auditLogEntry}
|
||||
* onSelect={(log) => openDetailModal(log)}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function AuditLogRow({ log, onSelect }: AuditLogRowProps) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
|
||||
@ -3,11 +3,35 @@ import { getAuditActionLabel, getAuditActionColor } from '@/hooks/useAudit';
|
||||
import { Activity, TrendingUp, Users, FileText } from 'lucide-react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/**
|
||||
* Props for the AuditStatsCard component.
|
||||
*/
|
||||
interface AuditStatsCardProps {
|
||||
/** Aggregated audit statistics including totals, breakdowns by action/entity, and daily trends */
|
||||
stats: AuditStats;
|
||||
/** Whether the component is in a loading state */
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an overview card with audit statistics and trends.
|
||||
*
|
||||
* @description Renders a dashboard card showing key audit metrics including total logs,
|
||||
* number of unique actions, entity types, and active users. Also displays breakdowns
|
||||
* by action type and entity type, plus a daily activity trend chart for the last 7 days.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.stats - Aggregated audit statistics object
|
||||
* @param props.isLoading - Whether to show loading skeleton instead of data
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <AuditStatsCard
|
||||
* stats={auditStats}
|
||||
* isLoading={isLoadingStats}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function AuditStatsCard({ stats, isLoading }: AuditStatsCardProps) {
|
||||
if (isLoading) {
|
||||
return (
|
||||
|
||||
@ -1,12 +1,51 @@
|
||||
import { DollarSign, Users, Calendar, TrendingUp, Clock, CheckCircle } from 'lucide-react';
|
||||
import { DashboardSummary, TopEarner, PeriodEarnings } from '../../services/commissions/dashboard.api';
|
||||
|
||||
/**
|
||||
* Props for the CommissionsDashboard component.
|
||||
*/
|
||||
interface CommissionsDashboardProps {
|
||||
/** Summary data containing totals for pending, approved, and paid commissions */
|
||||
summary: DashboardSummary;
|
||||
/** List of top earning users with their rankings and total earned amounts */
|
||||
topEarners: TopEarner[];
|
||||
/** List of recent commission periods with their earnings data */
|
||||
periodEarnings: PeriodEarnings[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a comprehensive dashboard for commission management.
|
||||
* Shows summary cards with pending, approved, paid commissions and active schemes,
|
||||
* along with top earners ranking and recent period performance.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.summary - Aggregated commission statistics including totals and counts
|
||||
* @param props.topEarners - Ranked list of users by earnings
|
||||
* @param props.periodEarnings - Historical period data with amounts and statuses
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <CommissionsDashboard
|
||||
* summary={{
|
||||
* total_pending: 5000,
|
||||
* pending_count: 10,
|
||||
* total_approved: 15000,
|
||||
* approved_count: 25,
|
||||
* total_paid: 50000,
|
||||
* paid_count: 100,
|
||||
* active_schemes: 5,
|
||||
* active_assignments: 20,
|
||||
* currency: 'USD'
|
||||
* }}
|
||||
* topEarners={[
|
||||
* { user_id: '1', user_name: 'John Doe', total_earned: 10000, entries_count: 50, rank: 1 }
|
||||
* ]}
|
||||
* periodEarnings={[
|
||||
* { period_id: '1', period_name: 'January 2026', starts_at: '2026-01-01', ends_at: '2026-01-31', total_amount: 25000, status: 'paid' }
|
||||
* ]}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function CommissionsDashboard({ summary, topEarners, periodEarnings }: CommissionsDashboardProps) {
|
||||
const formatCurrency = (amount: number) =>
|
||||
new Intl.NumberFormat('en-US', { style: 'currency', currency: summary.currency }).format(amount);
|
||||
|
||||
@ -1,13 +1,45 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
/**
|
||||
* Props for the EarningsCard component.
|
||||
*/
|
||||
interface EarningsCardProps {
|
||||
/** Title displayed above the value (e.g., "Total Earnings") */
|
||||
title: string;
|
||||
/** Main value to display (e.g., "$1,500.00") */
|
||||
value: string;
|
||||
/** Icon element to display on the right side of the card */
|
||||
icon: ReactNode;
|
||||
/** Color theme for the card styling */
|
||||
color: 'blue' | 'green' | 'yellow' | 'purple' | 'red';
|
||||
/** Optional subtitle displayed below the value */
|
||||
subtitle?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a styled card for showing earnings or commission statistics.
|
||||
* Features a title, large value display, colored icon, and optional subtitle.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.title - Label describing the statistic
|
||||
* @param props.value - Formatted value to display prominently
|
||||
* @param props.icon - React node for the icon (e.g., from lucide-react)
|
||||
* @param props.color - Color theme affecting the value text and icon background
|
||||
* @param props.subtitle - Additional context displayed below the value
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* import { DollarSign } from 'lucide-react';
|
||||
*
|
||||
* <EarningsCard
|
||||
* title="Total Earnings"
|
||||
* value="$12,500.00"
|
||||
* icon={<DollarSign className="h-6 w-6" />}
|
||||
* color="green"
|
||||
* subtitle="Last 30 days"
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function EarningsCard({ title, value, icon, color, subtitle }: EarningsCardProps) {
|
||||
const colorClasses = {
|
||||
blue: 'bg-blue-100 text-blue-600',
|
||||
|
||||
@ -2,17 +2,60 @@ import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { CommissionEntry } from '../../services/commissions/entries.api';
|
||||
import { EntryStatusBadge } from './EntryStatusBadge';
|
||||
|
||||
/**
|
||||
* Props for the EntriesList component.
|
||||
*/
|
||||
interface EntriesListProps {
|
||||
/** Array of commission entries to display in the table */
|
||||
entries: CommissionEntry[];
|
||||
/** Total number of entries across all pages */
|
||||
total: number;
|
||||
/** Current page number (1-indexed) */
|
||||
page: number;
|
||||
/** Total number of pages available */
|
||||
totalPages: number;
|
||||
/** Array of selected entry IDs for bulk operations */
|
||||
selectedEntries: string[];
|
||||
/** Callback when selection changes (for bulk approve/reject) */
|
||||
onSelectionChange: (ids: string[]) => void;
|
||||
/** Callback when page changes for pagination */
|
||||
onPageChange: (page: number) => void;
|
||||
/** Callback to refresh the entries list */
|
||||
onRefresh: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a paginated table of commission entries with selection support.
|
||||
* Allows selecting pending entries for bulk approval/rejection operations.
|
||||
* Shows entry details including date, user, reference, amounts, and status.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.entries - Commission entries to display
|
||||
* @param props.total - Total entry count for pagination info
|
||||
* @param props.page - Current page number
|
||||
* @param props.totalPages - Total pages for pagination controls
|
||||
* @param props.selectedEntries - Currently selected entry IDs
|
||||
* @param props.onSelectionChange - Handler for selection updates
|
||||
* @param props.onPageChange - Handler for page navigation
|
||||
* @param props.onRefresh - Handler to refresh data after operations
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const [selected, setSelected] = useState<string[]>([]);
|
||||
* const [page, setPage] = useState(1);
|
||||
*
|
||||
* <EntriesList
|
||||
* entries={commissionEntries}
|
||||
* total={100}
|
||||
* page={page}
|
||||
* totalPages={10}
|
||||
* selectedEntries={selected}
|
||||
* onSelectionChange={setSelected}
|
||||
* onPageChange={setPage}
|
||||
* onRefresh={() => refetch()}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function EntriesList({
|
||||
entries,
|
||||
total,
|
||||
|
||||
@ -1,9 +1,32 @@
|
||||
import { EntryStatus } from '../../services/commissions/entries.api';
|
||||
|
||||
/**
|
||||
* Props for the EntryStatusBadge component.
|
||||
*/
|
||||
interface EntryStatusBadgeProps {
|
||||
/** The status of the commission entry */
|
||||
status: EntryStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a colored badge indicating the status of a commission entry.
|
||||
* Each status has a distinct color for quick visual identification:
|
||||
* - pending: yellow
|
||||
* - approved: green
|
||||
* - rejected: red
|
||||
* - paid: blue
|
||||
* - cancelled: gray
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.status - The entry status to display
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <EntryStatusBadge status="pending" />
|
||||
* <EntryStatusBadge status="approved" />
|
||||
* <EntryStatusBadge status="paid" />
|
||||
* ```
|
||||
*/
|
||||
export function EntryStatusBadge({ status }: EntryStatusBadgeProps) {
|
||||
const getStatusConfig = (status: EntryStatus) => {
|
||||
switch (status) {
|
||||
|
||||
@ -2,15 +2,53 @@ import { ChevronLeft, ChevronRight, Lock, Unlock, DollarSign, Clock, CheckCircle
|
||||
import { CommissionPeriod, PeriodStatus } from '../../services/commissions/periods.api';
|
||||
import { useClosePeriod, useReopenPeriod, useMarkPeriodPaid } from '../../hooks/commissions';
|
||||
|
||||
/**
|
||||
* Props for the PeriodManager component.
|
||||
*/
|
||||
interface PeriodManagerProps {
|
||||
/** Array of commission periods to display */
|
||||
periods: CommissionPeriod[];
|
||||
/** Total number of periods across all pages */
|
||||
total: number;
|
||||
/** Current page number (1-indexed) */
|
||||
page: number;
|
||||
/** Total number of pages available */
|
||||
totalPages: number;
|
||||
/** Callback when page changes for pagination */
|
||||
onPageChange: (page: number) => void;
|
||||
/** Callback to refresh the periods list after operations */
|
||||
onRefresh: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages commission periods with lifecycle operations.
|
||||
* Displays periods in a table with their date ranges, entry counts, and totals.
|
||||
* Provides actions to close, reopen, and mark periods as paid based on status.
|
||||
*
|
||||
* Period lifecycle: open -> closed -> processing -> paid
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.periods - Commission periods to display
|
||||
* @param props.total - Total period count for pagination info
|
||||
* @param props.page - Current page number
|
||||
* @param props.totalPages - Total pages for pagination controls
|
||||
* @param props.onPageChange - Handler for page navigation
|
||||
* @param props.onRefresh - Handler to refresh data after operations
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const [page, setPage] = useState(1);
|
||||
*
|
||||
* <PeriodManager
|
||||
* periods={commissionPeriods}
|
||||
* total={24}
|
||||
* page={page}
|
||||
* totalPages={3}
|
||||
* onPageChange={setPage}
|
||||
* onRefresh={() => refetch()}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function PeriodManager({
|
||||
periods,
|
||||
total,
|
||||
|
||||
@ -3,12 +3,50 @@ import { X, Plus, Trash2 } from 'lucide-react';
|
||||
import { useCreateScheme, useUpdateScheme } from '../../hooks/commissions';
|
||||
import { CreateSchemeDto, SchemeType, AppliesTo, TierConfig, CommissionScheme } from '../../services/commissions/schemes.api';
|
||||
|
||||
/**
|
||||
* Props for the SchemeForm component.
|
||||
*/
|
||||
interface SchemeFormProps {
|
||||
/** Existing scheme to edit, or undefined for creating a new scheme */
|
||||
scheme?: CommissionScheme;
|
||||
/** Callback when the form is closed/cancelled */
|
||||
onClose: () => void;
|
||||
/** Callback when the scheme is successfully created/updated */
|
||||
onSuccess: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal form for creating or editing commission schemes.
|
||||
* Supports three scheme types: percentage, fixed amount, and tiered.
|
||||
* Includes configuration for rate, amounts, tiers, application scope, and limits.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.scheme - Existing scheme for editing (omit for create mode)
|
||||
* @param props.onClose - Handler to close the modal
|
||||
* @param props.onSuccess - Handler called after successful save
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Create mode
|
||||
* <SchemeForm
|
||||
* onClose={() => setShowForm(false)}
|
||||
* onSuccess={() => {
|
||||
* setShowForm(false);
|
||||
* refetch();
|
||||
* }}
|
||||
* />
|
||||
*
|
||||
* // Edit mode
|
||||
* <SchemeForm
|
||||
* scheme={existingScheme}
|
||||
* onClose={() => setShowForm(false)}
|
||||
* onSuccess={() => {
|
||||
* setShowForm(false);
|
||||
* refetch();
|
||||
* }}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function SchemeForm({ scheme, onClose, onSuccess }: SchemeFormProps) {
|
||||
const isEditing = !!scheme;
|
||||
const createMutation = useCreateScheme();
|
||||
|
||||
@ -2,15 +2,51 @@ import { Pencil, Trash2, Copy, ToggleLeft, ToggleRight, ChevronLeft, ChevronRigh
|
||||
import { CommissionScheme } from '../../services/commissions/schemes.api';
|
||||
import { useDeleteScheme, useDuplicateScheme, useToggleSchemeActive } from '../../hooks/commissions';
|
||||
|
||||
/**
|
||||
* Props for the SchemesList component.
|
||||
*/
|
||||
interface SchemesListProps {
|
||||
/** Array of commission schemes to display in the table */
|
||||
schemes: CommissionScheme[];
|
||||
/** Total number of schemes across all pages */
|
||||
total: number;
|
||||
/** Current page number (1-indexed) */
|
||||
page: number;
|
||||
/** Total number of pages available */
|
||||
totalPages: number;
|
||||
/** Callback when page changes for pagination */
|
||||
onPageChange: (page: number) => void;
|
||||
/** Callback to refresh the schemes list after operations */
|
||||
onRefresh: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a paginated table of commission schemes with management actions.
|
||||
* Shows scheme details including name, type, rate/amount, application scope, and status.
|
||||
* Provides actions to toggle active status, duplicate, and delete schemes.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.schemes - Commission schemes to display
|
||||
* @param props.total - Total scheme count for pagination info
|
||||
* @param props.page - Current page number
|
||||
* @param props.totalPages - Total pages for pagination controls
|
||||
* @param props.onPageChange - Handler for page navigation
|
||||
* @param props.onRefresh - Handler to refresh data after operations
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const [page, setPage] = useState(1);
|
||||
*
|
||||
* <SchemesList
|
||||
* schemes={commissionSchemes}
|
||||
* total={15}
|
||||
* page={page}
|
||||
* totalPages={2}
|
||||
* onPageChange={setPage}
|
||||
* onRefresh={() => refetch()}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function SchemesList({
|
||||
schemes,
|
||||
total,
|
||||
|
||||
@ -9,14 +9,59 @@ import { MoreVertical, Pencil, Trash2, ToggleLeft, ToggleRight, Copy } from 'luc
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/**
|
||||
* Props for the FeatureFlagCard component.
|
||||
*/
|
||||
interface FeatureFlagCardProps {
|
||||
/** The feature flag data to display */
|
||||
flag: FeatureFlag;
|
||||
/** Callback invoked when the edit action is triggered */
|
||||
onEdit: (flag: FeatureFlag) => void;
|
||||
/** Callback invoked when the delete action is triggered */
|
||||
onDelete: (flag: FeatureFlag) => void;
|
||||
/** Callback invoked when the toggle action is triggered */
|
||||
onToggle: (flag: FeatureFlag) => void;
|
||||
/** Indicates if the toggle operation is in progress */
|
||||
isToggling?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a feature flag as a card with its details, status, and action menu.
|
||||
*
|
||||
* @description Renders a card showing the feature flag's name, key, description,
|
||||
* type, scope, category, rollout percentage, and default value. Includes actions
|
||||
* for toggling, editing, and deleting the flag, plus a copy-to-clipboard button
|
||||
* for the flag key.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.flag - The feature flag data to display
|
||||
* @param props.onEdit - Callback invoked when the edit action is triggered
|
||||
* @param props.onDelete - Callback invoked when the delete action is triggered
|
||||
* @param props.onToggle - Callback invoked when the toggle action is triggered
|
||||
* @param props.isToggling - Indicates if the toggle operation is in progress
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <FeatureFlagCard
|
||||
* flag={{
|
||||
* id: '1',
|
||||
* key: 'enable_dark_mode',
|
||||
* name: 'Enable Dark Mode',
|
||||
* description: 'Allows users to switch to dark theme',
|
||||
* flag_type: 'boolean',
|
||||
* scope: 'user',
|
||||
* is_enabled: true,
|
||||
* default_value: false,
|
||||
* rollout_percentage: 100,
|
||||
* category: 'UI',
|
||||
* }}
|
||||
* onEdit={(flag) => console.log('Edit', flag)}
|
||||
* onDelete={(flag) => console.log('Delete', flag)}
|
||||
* onToggle={(flag) => console.log('Toggle', flag)}
|
||||
* isToggling={false}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function FeatureFlagCard({
|
||||
flag,
|
||||
onEdit,
|
||||
|
||||
@ -2,13 +2,21 @@ import { useState, useEffect } from 'react';
|
||||
import { FeatureFlag, FlagType, FlagScope, CreateFlagRequest, UpdateFlagRequest } from '@/services/api';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
|
||||
/**
|
||||
* Props for the FeatureFlagForm component.
|
||||
*/
|
||||
interface FeatureFlagFormProps {
|
||||
/** Existing flag data for editing mode; omit for create mode */
|
||||
flag?: FeatureFlag;
|
||||
/** Callback invoked on form submission with the flag data */
|
||||
onSubmit: (data: CreateFlagRequest | UpdateFlagRequest) => void;
|
||||
/** Callback invoked when the cancel button is clicked */
|
||||
onCancel: () => void;
|
||||
/** Indicates if the submit operation is in progress */
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
/** Available flag types for selection. */
|
||||
const FLAG_TYPES: { value: FlagType; label: string }[] = [
|
||||
{ value: 'boolean', label: 'Boolean' },
|
||||
{ value: 'string', label: 'String' },
|
||||
@ -16,6 +24,7 @@ const FLAG_TYPES: { value: FlagType; label: string }[] = [
|
||||
{ value: 'json', label: 'JSON' },
|
||||
];
|
||||
|
||||
/** Available flag scopes for selection. */
|
||||
const FLAG_SCOPES: { value: FlagScope; label: string; description: string }[] = [
|
||||
{ value: 'global', label: 'Global', description: 'Applies to all tenants and users' },
|
||||
{ value: 'tenant', label: 'Tenant', description: 'Can be overridden per tenant' },
|
||||
@ -23,6 +32,38 @@ const FLAG_SCOPES: { value: FlagScope; label: string; description: string }[] =
|
||||
{ value: 'plan', label: 'Plan', description: 'Based on subscription plan' },
|
||||
];
|
||||
|
||||
/**
|
||||
* Form for creating or editing a feature flag.
|
||||
*
|
||||
* @description Renders a form with fields for all feature flag properties including
|
||||
* key (create only), name, description, type, scope, default value, category,
|
||||
* rollout percentage, and enabled status. Validates input and parses the default
|
||||
* value based on the selected flag type.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.flag - Existing flag data for editing mode; omit for create mode
|
||||
* @param props.onSubmit - Callback invoked on form submission with the flag data
|
||||
* @param props.onCancel - Callback invoked when the cancel button is clicked
|
||||
* @param props.isLoading - Indicates if the submit operation is in progress
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Create mode
|
||||
* <FeatureFlagForm
|
||||
* onSubmit={(data) => createFlag(data)}
|
||||
* onCancel={() => closeModal()}
|
||||
* isLoading={isCreating}
|
||||
* />
|
||||
*
|
||||
* // Edit mode
|
||||
* <FeatureFlagForm
|
||||
* flag={existingFlag}
|
||||
* onSubmit={(data) => updateFlag(existingFlag.id, data)}
|
||||
* onCancel={() => closeModal()}
|
||||
* isLoading={isUpdating}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function FeatureFlagForm({ flag, onSubmit, onCancel, isLoading }: FeatureFlagFormProps) {
|
||||
const isEditing = !!flag;
|
||||
|
||||
|
||||
@ -3,14 +3,53 @@ import { FeatureFlag, TenantFlag } from '@/services/api';
|
||||
import { Plus, Trash2, ToggleLeft, ToggleRight, AlertCircle } from 'lucide-react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/**
|
||||
* Props for the TenantOverridesPanel component.
|
||||
*/
|
||||
interface TenantOverridesPanelProps {
|
||||
/** List of all available feature flags */
|
||||
flags: FeatureFlag[];
|
||||
/** List of current tenant-specific flag overrides */
|
||||
overrides: TenantFlag[];
|
||||
/** Callback invoked when adding a new override */
|
||||
onAdd: (flagId: string, isEnabled: boolean, value?: any) => void;
|
||||
/** Callback invoked when removing an existing override */
|
||||
onRemove: (flagId: string) => void;
|
||||
/** Indicates if an operation is in progress */
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Panel for managing tenant-specific feature flag overrides.
|
||||
*
|
||||
* @description Displays a list of tenant overrides with the ability to add new
|
||||
* overrides and remove existing ones. Allows tenants to customize global feature
|
||||
* flag settings for their organization. Only flags that don't already have an
|
||||
* override are available for selection.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.flags - List of all available feature flags
|
||||
* @param props.overrides - List of current tenant-specific flag overrides
|
||||
* @param props.onAdd - Callback invoked when adding a new override
|
||||
* @param props.onRemove - Callback invoked when removing an existing override
|
||||
* @param props.isLoading - Indicates if an operation is in progress
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <TenantOverridesPanel
|
||||
* flags={[
|
||||
* { id: '1', key: 'enable_dark_mode', name: 'Enable Dark Mode', ... },
|
||||
* { id: '2', key: 'beta_features', name: 'Beta Features', ... },
|
||||
* ]}
|
||||
* overrides={[
|
||||
* { id: 'o1', flag_id: '1', is_enabled: true, ... },
|
||||
* ]}
|
||||
* onAdd={(flagId, isEnabled) => console.log('Add override', flagId, isEnabled)}
|
||||
* onRemove={(flagId) => console.log('Remove override', flagId)}
|
||||
* isLoading={false}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function TenantOverridesPanel({
|
||||
flags,
|
||||
overrides,
|
||||
|
||||
@ -1,10 +1,35 @@
|
||||
import React from 'react';
|
||||
import { useDevices, usePushNotifications, UserDevice } from '@/hooks/usePushNotifications';
|
||||
|
||||
/**
|
||||
* Props for the DevicesManager component.
|
||||
*/
|
||||
interface DevicesManagerProps {
|
||||
/**
|
||||
* Whether to display the header section with title and description.
|
||||
* @default true
|
||||
*/
|
||||
showHeader?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages user devices registered for push notifications.
|
||||
*
|
||||
* @description Displays a list of devices registered to receive push notifications,
|
||||
* allowing users to view device details, register new devices, and unregister existing ones.
|
||||
* Shows device type icons, activity status, and last used timestamp for each device.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.showHeader - Whether to display the header section with title and "Add device" button
|
||||
*
|
||||
* @example
|
||||
* // Basic usage with header
|
||||
* <DevicesManager />
|
||||
*
|
||||
* @example
|
||||
* // Without header (for embedding in settings pages)
|
||||
* <DevicesManager showHeader={false} />
|
||||
*/
|
||||
export function DevicesManager({ showHeader = true }: DevicesManagerProps) {
|
||||
const { data: devices, isLoading, error, refetch } = useDevices();
|
||||
const { unregisterDevice, isSubscribed, requestPermission, isLoading: pushLoading } = usePushNotifications();
|
||||
|
||||
@ -4,6 +4,28 @@ import clsx from 'clsx';
|
||||
import { useUnreadNotificationsCount } from '@/hooks/useData';
|
||||
import { NotificationDrawer } from './NotificationDrawer';
|
||||
|
||||
/**
|
||||
* A bell icon button that displays the unread notification count and opens the notification drawer.
|
||||
*
|
||||
* @description Renders a clickable bell icon that shows a badge with the unread notification count.
|
||||
* When clicked, it opens the NotificationDrawer component as a slide-in panel.
|
||||
* The bell icon color changes based on whether there are unread notifications.
|
||||
*
|
||||
* @example
|
||||
* // Basic usage in a header/navbar
|
||||
* <header>
|
||||
* <nav>
|
||||
* <NotificationBell />
|
||||
* </nav>
|
||||
* </header>
|
||||
*
|
||||
* @example
|
||||
* // Usage in a toolbar
|
||||
* <div className="toolbar">
|
||||
* <NotificationBell />
|
||||
* <UserMenu />
|
||||
* </div>
|
||||
*/
|
||||
export function NotificationBell() {
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
const { data } = useUnreadNotificationsCount();
|
||||
|
||||
@ -4,11 +4,41 @@ import clsx from 'clsx';
|
||||
import { useNotifications, useMarkNotificationAsRead, useMarkAllNotificationsAsRead } from '@/hooks/useData';
|
||||
import { NotificationItem } from './NotificationItem';
|
||||
|
||||
/**
|
||||
* Props for the NotificationDrawer component.
|
||||
*/
|
||||
interface NotificationDrawerProps {
|
||||
/**
|
||||
* Controls the visibility of the drawer.
|
||||
*/
|
||||
isOpen: boolean;
|
||||
/**
|
||||
* Callback function invoked when the drawer should be closed.
|
||||
*/
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A slide-in drawer panel that displays user notifications.
|
||||
*
|
||||
* @description Renders a full-height drawer that slides in from the right side of the screen.
|
||||
* Displays a list of notifications with the ability to mark individual or all notifications as read.
|
||||
* Includes a backdrop overlay, loading state, and empty state handling.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.isOpen - Whether the drawer is currently open/visible
|
||||
* @param props.onClose - Callback to close the drawer (triggered by backdrop click or close button)
|
||||
*
|
||||
* @example
|
||||
* // Controlled drawer usage
|
||||
* const [isOpen, setIsOpen] = useState(false);
|
||||
*
|
||||
* <button onClick={() => setIsOpen(true)}>Open Notifications</button>
|
||||
* <NotificationDrawer
|
||||
* isOpen={isOpen}
|
||||
* onClose={() => setIsOpen(false)}
|
||||
* />
|
||||
*/
|
||||
export function NotificationDrawer({ isOpen, onClose }: NotificationDrawerProps) {
|
||||
const navigate = useNavigate();
|
||||
const { data, isLoading } = useNotifications(1, 20);
|
||||
|
||||
@ -3,9 +3,24 @@ import { Bell, AlertCircle, CheckCircle, Info, AlertTriangle } from 'lucide-reac
|
||||
import clsx from 'clsx';
|
||||
import type { Notification } from '@/hooks/useData';
|
||||
|
||||
/**
|
||||
* Props for the NotificationItem component.
|
||||
*/
|
||||
interface NotificationItemProps {
|
||||
/**
|
||||
* The notification object to display.
|
||||
*/
|
||||
notification: Notification;
|
||||
/**
|
||||
* Callback invoked when the notification is clicked to mark it as read.
|
||||
* @param id - The notification ID
|
||||
*/
|
||||
onRead: (id: string) => void;
|
||||
/**
|
||||
* Optional callback invoked when the notification has an action URL.
|
||||
* If provided and the notification has an action_url, navigation will occur.
|
||||
* @param url - The URL to navigate to
|
||||
*/
|
||||
onNavigate?: (url: string) => void;
|
||||
}
|
||||
|
||||
@ -25,6 +40,33 @@ const typeColors: Record<string, string> = {
|
||||
default: 'text-secondary-500 bg-secondary-50 dark:bg-secondary-800',
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders a single notification item with icon, title, message, and timestamp.
|
||||
*
|
||||
* @description Displays a notification with type-based icon and coloring (info, success, warning, error).
|
||||
* Shows read/unread status visually and handles click events to mark as read and optionally navigate.
|
||||
* Uses relative time formatting for the timestamp (e.g., "5 minutes ago").
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.notification - The notification data including id, title, message, type, and timestamps
|
||||
* @param props.onRead - Callback to mark the notification as read when clicked
|
||||
* @param props.onNavigate - Optional callback for navigation when notification has an action URL
|
||||
*
|
||||
* @example
|
||||
* // Basic usage
|
||||
* <NotificationItem
|
||||
* notification={{
|
||||
* id: '1',
|
||||
* title: 'New message',
|
||||
* message: 'You have a new message from John',
|
||||
* type: 'info',
|
||||
* created_at: '2026-01-25T10:00:00Z',
|
||||
* read_at: null
|
||||
* }}
|
||||
* onRead={(id) => markAsRead(id)}
|
||||
* onNavigate={(url) => navigate(url)}
|
||||
* />
|
||||
*/
|
||||
export function NotificationItem({ notification, onRead, onNavigate }: NotificationItemProps) {
|
||||
const isRead = !!notification.read_at;
|
||||
const Icon = typeIcons[notification.type] || typeIcons.default;
|
||||
|
||||
@ -1,10 +1,43 @@
|
||||
import React from 'react';
|
||||
import { usePushNotifications } from '@/hooks/usePushNotifications';
|
||||
|
||||
/**
|
||||
* Props for the PushPermissionBanner component.
|
||||
*/
|
||||
interface PushPermissionBannerProps {
|
||||
/**
|
||||
* Optional callback invoked when the user dismisses the banner.
|
||||
* The dismissal is also persisted to localStorage.
|
||||
*/
|
||||
onDismiss?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A promotional banner prompting users to enable push notifications.
|
||||
*
|
||||
* @description Displays a banner encouraging users to enable browser push notifications.
|
||||
* Automatically hides when: push notifications are not supported, already subscribed,
|
||||
* permission was denied, or the banner was previously dismissed. Stores dismissal
|
||||
* state in localStorage to avoid showing repeatedly.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.onDismiss - Optional callback when user dismisses the banner (via "Not now" or close button)
|
||||
*
|
||||
* @example
|
||||
* // Basic usage at the top of a dashboard
|
||||
* <div className="dashboard">
|
||||
* <PushPermissionBanner />
|
||||
* <DashboardContent />
|
||||
* </div>
|
||||
*
|
||||
* @example
|
||||
* // With custom dismiss handling
|
||||
* <PushPermissionBanner
|
||||
* onDismiss={() => {
|
||||
* analytics.track('push_banner_dismissed');
|
||||
* }}
|
||||
* />
|
||||
*/
|
||||
export function PushPermissionBanner({ onDismiss }: PushPermissionBannerProps) {
|
||||
const {
|
||||
isSupported,
|
||||
|
||||
@ -3,12 +3,45 @@ import { X } from 'lucide-react';
|
||||
import { useCreateActivity } from '../../hooks/sales';
|
||||
import { CreateActivityDto, ActivityType } from '../../services/sales/activities.api';
|
||||
|
||||
/**
|
||||
* Props for the ActivityForm component
|
||||
*/
|
||||
interface ActivityFormProps {
|
||||
/** Optional lead ID to associate the activity with */
|
||||
leadId?: string;
|
||||
/** Optional opportunity ID to associate the activity with */
|
||||
opportunityId?: string;
|
||||
/** Callback function triggered when the form is closed or after successful submission */
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal form component for creating sales activities such as calls, meetings, tasks, emails, or notes.
|
||||
* Provides fields for activity type, subject, description, due date, and duration.
|
||||
* Shows conditional fields based on activity type (e.g., call direction for calls, location for meetings).
|
||||
* Activities can be associated with a lead, opportunity, or both.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.leadId - Optional lead ID to link the activity
|
||||
* @param props.opportunityId - Optional opportunity ID to link the activity
|
||||
* @param props.onClose - Callback to close the modal
|
||||
* @returns A modal form for activity creation
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Activity for a lead
|
||||
* <ActivityForm
|
||||
* leadId="lead-123"
|
||||
* onClose={() => setShowForm(false)}
|
||||
* />
|
||||
*
|
||||
* // Activity for an opportunity
|
||||
* <ActivityForm
|
||||
* opportunityId="opp-456"
|
||||
* onClose={() => setShowForm(false)}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function ActivityForm({ leadId, opportunityId, onClose }: ActivityFormProps) {
|
||||
const [formData, setFormData] = useState<CreateActivityDto>({
|
||||
type: 'task',
|
||||
|
||||
@ -1,10 +1,48 @@
|
||||
import { Phone, Users, CheckSquare, Mail, FileText, Clock, CheckCircle } from 'lucide-react';
|
||||
import { Activity, ActivityType, ActivityStatus } from '../../services/sales/activities.api';
|
||||
|
||||
/**
|
||||
* Props for the ActivityTimeline component
|
||||
*/
|
||||
interface ActivityTimelineProps {
|
||||
/** Array of activity objects to display in chronological order */
|
||||
activities: Activity[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a vertical timeline of sales activities with visual differentiation by type.
|
||||
* Each activity shows an icon based on type (call, meeting, task, email, note),
|
||||
* subject, description, status indicator, date, and optional outcome.
|
||||
* Uses color-coded icons and a connecting timeline line for visual hierarchy.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.activities - Array of activities to display in the timeline
|
||||
* @returns A timeline component showing activity history
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <ActivityTimeline
|
||||
* activities={[
|
||||
* {
|
||||
* id: '1',
|
||||
* type: 'call',
|
||||
* subject: 'Follow-up call',
|
||||
* status: 'completed',
|
||||
* created_at: '2026-01-15',
|
||||
* outcome: 'Scheduled demo for next week'
|
||||
* },
|
||||
* {
|
||||
* id: '2',
|
||||
* type: 'meeting',
|
||||
* subject: 'Product demo',
|
||||
* status: 'pending',
|
||||
* due_date: '2026-01-22',
|
||||
* created_at: '2026-01-15'
|
||||
* }
|
||||
* ]}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function ActivityTimeline({ activities }: ActivityTimelineProps) {
|
||||
const getIcon = (type: ActivityType) => {
|
||||
switch (type) {
|
||||
|
||||
@ -1,9 +1,37 @@
|
||||
import { ConversionRates } from '../../services/sales/dashboard.api';
|
||||
|
||||
/**
|
||||
* Props for the ConversionFunnel component
|
||||
*/
|
||||
interface ConversionFunnelProps {
|
||||
/** Conversion rate data including stage-to-stage rates and breakdown by source */
|
||||
conversion: ConversionRates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a visual sales conversion funnel with stage breakdown and rates.
|
||||
* Shows a tapered funnel visualization from Leads to Opportunities to Won deals.
|
||||
* Includes conversion rate metrics and optional breakdown by lead source.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.conversion - Conversion rate data for funnel visualization
|
||||
* @returns A funnel chart component with conversion metrics
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <ConversionFunnel
|
||||
* conversion={{
|
||||
* leadToOpportunity: 30,
|
||||
* opportunityToWon: 40,
|
||||
* overall: 12,
|
||||
* bySource: [
|
||||
* { source: 'website', rate: 15 },
|
||||
* { source: 'referral', rate: 25 }
|
||||
* ]
|
||||
* }}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function ConversionFunnel({ conversion }: ConversionFunnelProps) {
|
||||
const funnelStages = [
|
||||
{ name: 'Leads', value: 100, color: 'bg-blue-500' },
|
||||
|
||||
@ -1,10 +1,39 @@
|
||||
import { Lead } from '../../services/sales/leads.api';
|
||||
import { Mail, Phone, Building, Briefcase, Globe, MapPin, Star } from 'lucide-react';
|
||||
|
||||
/**
|
||||
* Props for the LeadCard component
|
||||
*/
|
||||
interface LeadCardProps {
|
||||
/** The lead data object to display */
|
||||
lead: Lead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays detailed information about a sales lead in a card format.
|
||||
* Shows contact information, company details, address, lead status, source, and score.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.lead - The lead data object containing all lead information
|
||||
* @returns A card component displaying the lead's complete details
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <LeadCard
|
||||
* lead={{
|
||||
* id: '123',
|
||||
* first_name: 'John',
|
||||
* last_name: 'Doe',
|
||||
* email: 'john@example.com',
|
||||
* status: 'qualified',
|
||||
* source: 'website',
|
||||
* score: 85,
|
||||
* created_at: '2026-01-01',
|
||||
* updated_at: '2026-01-15'
|
||||
* }}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function LeadCard({ lead }: LeadCardProps) {
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
|
||||
@ -3,11 +3,38 @@ import { X } from 'lucide-react';
|
||||
import { useCreateLead, useUpdateLead } from '../../hooks/sales';
|
||||
import { Lead, CreateLeadDto, LeadSource, LeadStatus } from '../../services/sales/leads.api';
|
||||
|
||||
/**
|
||||
* Props for the LeadForm component
|
||||
*/
|
||||
interface LeadFormProps {
|
||||
/** Optional existing lead data for edit mode. If not provided, form operates in create mode */
|
||||
lead?: Lead;
|
||||
/** Callback function triggered when the form is closed or after successful submission */
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal form component for creating or editing sales leads.
|
||||
* Provides fields for lead contact information, company details, source, status, and notes.
|
||||
* Handles form validation and submission via React Query mutations.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.lead - Optional existing lead data for editing
|
||||
* @param props.onClose - Callback to close the modal
|
||||
* @returns A modal form for lead creation/editing
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Create mode
|
||||
* <LeadForm onClose={() => setShowForm(false)} />
|
||||
*
|
||||
* // Edit mode
|
||||
* <LeadForm
|
||||
* lead={existingLead}
|
||||
* onClose={() => setShowForm(false)}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function LeadForm({ lead, onClose }: LeadFormProps) {
|
||||
const [formData, setFormData] = useState<CreateLeadDto>({
|
||||
first_name: lead?.first_name || '',
|
||||
|
||||
@ -1,14 +1,46 @@
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Lead } from '../../services/sales/leads.api';
|
||||
|
||||
/**
|
||||
* Props for the LeadsList component
|
||||
*/
|
||||
interface LeadsListProps {
|
||||
/** Array of lead objects to display in the table */
|
||||
leads: Lead[];
|
||||
/** Total number of leads across all pages */
|
||||
total: number;
|
||||
/** Current page number (1-indexed) */
|
||||
page: number;
|
||||
/** Total number of available pages */
|
||||
totalPages: number;
|
||||
/** Callback function triggered when user navigates to a different page */
|
||||
onPageChange: (page: number) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a paginated table of sales leads with navigation support.
|
||||
* Shows lead name, company, status, source, score, and creation date.
|
||||
* Clicking a row navigates to the lead detail page.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.leads - Array of leads to display
|
||||
* @param props.total - Total lead count for pagination info
|
||||
* @param props.page - Current page number
|
||||
* @param props.totalPages - Total pages available
|
||||
* @param props.onPageChange - Page navigation callback
|
||||
* @returns A table component with pagination controls
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <LeadsList
|
||||
* leads={leadsData}
|
||||
* total={100}
|
||||
* page={1}
|
||||
* totalPages={5}
|
||||
* onPageChange={(newPage) => setCurrentPage(newPage)}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function LeadsList({ leads, total, page, totalPages, onPageChange }: LeadsListProps) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
||||
@ -1,12 +1,45 @@
|
||||
import { DollarSign, Calendar, User } from 'lucide-react';
|
||||
import { Opportunity } from '../../services/sales/opportunities.api';
|
||||
|
||||
/**
|
||||
* Props for the OpportunityCard component
|
||||
*/
|
||||
interface OpportunityCardProps {
|
||||
/** The opportunity data object to display */
|
||||
opportunity: Opportunity;
|
||||
/** Whether to render in compact mode with minimal information. Defaults to false */
|
||||
compact?: boolean;
|
||||
/** Optional click handler for card interaction */
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a sales opportunity in a card format with two display modes.
|
||||
* Full mode shows name, company, amount, probability, close date, and contact.
|
||||
* Compact mode shows only name and amount for use in dense layouts like pipeline boards.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.opportunity - The opportunity data to display
|
||||
* @param props.compact - Enable compact display mode
|
||||
* @param props.onClick - Click handler for card interaction
|
||||
* @returns A card component displaying opportunity information
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Full display mode
|
||||
* <OpportunityCard
|
||||
* opportunity={opportunityData}
|
||||
* onClick={() => navigate(`/opportunities/${opportunityData.id}`)}
|
||||
* />
|
||||
*
|
||||
* // Compact mode for pipeline boards
|
||||
* <OpportunityCard
|
||||
* opportunity={opportunityData}
|
||||
* compact
|
||||
* onClick={() => handleSelect(opportunityData)}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function OpportunityCard({ opportunity, compact = false, onClick }: OpportunityCardProps) {
|
||||
const formatCurrency = (amount: number) => {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
|
||||
@ -3,11 +3,39 @@ import { X } from 'lucide-react';
|
||||
import { useCreateOpportunity, useUpdateOpportunity } from '../../hooks/sales';
|
||||
import { Opportunity, CreateOpportunityDto, OpportunityStage } from '../../services/sales/opportunities.api';
|
||||
|
||||
/**
|
||||
* Props for the OpportunityForm component
|
||||
*/
|
||||
interface OpportunityFormProps {
|
||||
/** Optional existing opportunity data for edit mode. If not provided, form operates in create mode */
|
||||
opportunity?: Opportunity;
|
||||
/** Callback function triggered when the form is closed or after successful submission */
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal form component for creating or editing sales opportunities.
|
||||
* Provides fields for opportunity name, amount, currency, probability, stage,
|
||||
* expected close date, contact information, and notes.
|
||||
* Handles form validation and submission via React Query mutations.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.opportunity - Optional existing opportunity data for editing
|
||||
* @param props.onClose - Callback to close the modal
|
||||
* @returns A modal form for opportunity creation/editing
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Create mode
|
||||
* <OpportunityForm onClose={() => setShowForm(false)} />
|
||||
*
|
||||
* // Edit mode
|
||||
* <OpportunityForm
|
||||
* opportunity={existingOpportunity}
|
||||
* onClose={() => setShowForm(false)}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function OpportunityForm({ opportunity, onClose }: OpportunityFormProps) {
|
||||
const [formData, setFormData] = useState<CreateOpportunityDto>({
|
||||
name: opportunity?.name || '',
|
||||
|
||||
@ -2,10 +2,40 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { PipelineView } from '../../services/sales/opportunities.api';
|
||||
import { OpportunityCard } from './OpportunityCard';
|
||||
|
||||
/**
|
||||
* Props for the PipelineBoard component
|
||||
*/
|
||||
interface PipelineBoardProps {
|
||||
/** Array of pipeline stages with their opportunities, counts, and totals */
|
||||
stages: PipelineView[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a Kanban-style sales pipeline board with opportunities organized by stage.
|
||||
* Open stages (prospecting, qualification, proposal, negotiation) are shown as columns.
|
||||
* Closed stages (won/lost) are displayed in a separate grid below.
|
||||
* Each column shows stage name, opportunity count, and total value.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.stages - Array of pipeline stage data with opportunities
|
||||
* @returns A Kanban board component for visualizing the sales pipeline
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <PipelineBoard
|
||||
* stages={[
|
||||
* {
|
||||
* stage: 'prospecting',
|
||||
* stageName: 'Prospecting',
|
||||
* count: 5,
|
||||
* totalAmount: 50000,
|
||||
* opportunities: [...]
|
||||
* },
|
||||
* // ... other stages
|
||||
* ]}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function PipelineBoard({ stages }: PipelineBoardProps) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
||||
@ -2,11 +2,44 @@ import { TrendingUp, Users, DollarSign, Target, ArrowUpRight, ArrowDownRight } f
|
||||
import { SalesSummary, ConversionRates } from '../../services/sales/dashboard.api';
|
||||
import { ConversionFunnel } from './ConversionFunnel';
|
||||
|
||||
/**
|
||||
* Props for the SalesDashboard component
|
||||
*/
|
||||
interface SalesDashboardProps {
|
||||
/** Sales summary data including leads, opportunities, pipeline, and activities metrics */
|
||||
summary: SalesSummary;
|
||||
/** Conversion rate data for the sales funnel visualization */
|
||||
conversion: ConversionRates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a comprehensive sales dashboard with key metrics and visualizations.
|
||||
* Includes stat cards for total leads, open opportunities, won value, and win rate.
|
||||
* Shows pipeline breakdown by stage, conversion funnel, and activities overview.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.summary - Sales summary metrics data
|
||||
* @param props.conversion - Conversion rates for funnel visualization
|
||||
* @returns A dashboard component with sales KPIs and charts
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <SalesDashboard
|
||||
* summary={{
|
||||
* leads: { total: 150, conversionRate: 25 },
|
||||
* opportunities: { open: 45, won: 12, wonValue: 125000, winRate: 35, avgDealSize: 10000 },
|
||||
* pipeline: { totalValue: 500000, byStage: [...] },
|
||||
* activities: { total: 200, pending: 30, completed: 160, overdue: 10 }
|
||||
* }}
|
||||
* conversion={{
|
||||
* leadToOpportunity: 30,
|
||||
* opportunityToWon: 40,
|
||||
* overall: 12,
|
||||
* bySource: [...]
|
||||
* }}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function SalesDashboard({ summary, conversion }: SalesDashboardProps) {
|
||||
const formatCurrency = (amount: number) => {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
|
||||
@ -15,10 +15,17 @@ import {
|
||||
import { StorageFile, storageApi } from '@/services/api';
|
||||
import { useDeleteFile } from '@/hooks/useStorage';
|
||||
|
||||
/**
|
||||
* Props for the FileItem component.
|
||||
*/
|
||||
interface FileItemProps {
|
||||
/** The storage file object containing metadata and content info */
|
||||
file: StorageFile;
|
||||
/** Display mode: 'grid' for thumbnail cards, 'list' for horizontal rows */
|
||||
view?: 'grid' | 'list';
|
||||
/** Callback fired after successful file deletion */
|
||||
onDelete?: () => void;
|
||||
/** Callback fired when user requests file preview (images only) */
|
||||
onPreview?: (file: StorageFile) => void;
|
||||
}
|
||||
|
||||
@ -56,6 +63,32 @@ function VisibilityIcon({ visibility }: { visibility: string }) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a single file entry with download, delete, and preview actions.
|
||||
* Supports both grid (thumbnail cards) and list (horizontal rows) view modes.
|
||||
* Automatically shows appropriate icons based on file MIME type and handles
|
||||
* visibility indicators (private, tenant, public).
|
||||
*
|
||||
* @description Renders a file item with actions for download, delete, and preview.
|
||||
* @param props - The component props
|
||||
* @param props.file - The storage file object to display
|
||||
* @param props.view - Display mode: 'grid' or 'list' (default: 'list')
|
||||
* @param props.onDelete - Optional callback after successful deletion
|
||||
* @param props.onPreview - Optional callback for image preview requests
|
||||
*
|
||||
* @example
|
||||
* // Basic usage in list view
|
||||
* <FileItem file={myFile} />
|
||||
*
|
||||
* @example
|
||||
* // Grid view with preview handler
|
||||
* <FileItem
|
||||
* file={myFile}
|
||||
* view="grid"
|
||||
* onPreview={(file) => openPreviewModal(file)}
|
||||
* onDelete={() => refetchFiles()}
|
||||
* />
|
||||
*/
|
||||
export function FileItem({ file, view = 'list', onDelete, onPreview }: FileItemProps) {
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
const [downloading, setDownloading] = useState(false);
|
||||
|
||||
@ -4,12 +4,41 @@ import { useFiles } from '@/hooks/useStorage';
|
||||
import { StorageFile } from '@/services/api';
|
||||
import { FileItem } from './FileItem';
|
||||
|
||||
/**
|
||||
* Props for the FileList component.
|
||||
*/
|
||||
interface FileListProps {
|
||||
/** Optional folder path to filter files */
|
||||
folder?: string;
|
||||
/** Callback fired when user requests file preview */
|
||||
onPreview?: (file: StorageFile) => void;
|
||||
/** Additional CSS classes to apply to the container */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a paginated, searchable list of files with grid/list view toggle.
|
||||
* Fetches files from the storage API and provides search, pagination,
|
||||
* and view mode switching. Handles loading, empty, and error states.
|
||||
*
|
||||
* @description Renders a file browser with search, pagination, and view modes.
|
||||
* @param props - The component props
|
||||
* @param props.folder - Optional folder to filter files by
|
||||
* @param props.onPreview - Optional callback for file preview requests
|
||||
* @param props.className - Additional CSS classes for the container
|
||||
*
|
||||
* @example
|
||||
* // Basic usage
|
||||
* <FileList />
|
||||
*
|
||||
* @example
|
||||
* // With folder filter and preview handler
|
||||
* <FileList
|
||||
* folder="documents"
|
||||
* onPreview={(file) => setPreviewFile(file)}
|
||||
* className="mt-4"
|
||||
* />
|
||||
*/
|
||||
export function FileList({ folder, onPreview, className = '' }: FileListProps) {
|
||||
const [view, setView] = useState<'grid' | 'list'>('list');
|
||||
const [page, setPage] = useState(1);
|
||||
|
||||
@ -2,13 +2,23 @@ import { useState, useRef, useCallback } from 'react';
|
||||
import { Upload, X, File, Image, FileText, Table, AlertCircle } from 'lucide-react';
|
||||
import { useUploadFile } from '@/hooks/useStorage';
|
||||
|
||||
/**
|
||||
* Props for the FileUpload component.
|
||||
*/
|
||||
interface FileUploadProps {
|
||||
/** Target folder for uploaded files (default: 'files') */
|
||||
folder?: string;
|
||||
/** File visibility level: 'private', 'tenant', or 'public' (default: 'private') */
|
||||
visibility?: 'private' | 'tenant' | 'public';
|
||||
/** MIME types to accept (defaults to ALLOWED_TYPES) */
|
||||
accept?: string;
|
||||
maxSize?: number; // in bytes
|
||||
/** Maximum file size in bytes (default: 50MB) */
|
||||
maxSize?: number;
|
||||
/** Callback fired after successful upload with file id and filename */
|
||||
onSuccess?: (file: { id: string; filename: string }) => void;
|
||||
/** Callback fired when upload fails with the error */
|
||||
onError?: (error: Error) => void;
|
||||
/** Additional CSS classes to apply to the container */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@ -42,6 +52,37 @@ function formatBytes(bytes: number): string {
|
||||
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag-and-drop file upload component with progress indicator.
|
||||
* Supports file validation (type and size), visual drag feedback,
|
||||
* upload progress display, and error handling. Files can be selected
|
||||
* via drag-and-drop or click-to-browse.
|
||||
*
|
||||
* @description Renders a drag-and-drop zone for uploading files with progress.
|
||||
* @param props - The component props
|
||||
* @param props.folder - Target folder for uploads (default: 'files')
|
||||
* @param props.visibility - File visibility: 'private', 'tenant', or 'public'
|
||||
* @param props.accept - MIME types to accept
|
||||
* @param props.maxSize - Maximum file size in bytes (default: 50MB)
|
||||
* @param props.onSuccess - Callback after successful upload
|
||||
* @param props.onError - Callback on upload error
|
||||
* @param props.className - Additional CSS classes
|
||||
*
|
||||
* @example
|
||||
* // Basic usage
|
||||
* <FileUpload onSuccess={(file) => console.log('Uploaded:', file.id)} />
|
||||
*
|
||||
* @example
|
||||
* // With custom settings
|
||||
* <FileUpload
|
||||
* folder="avatars"
|
||||
* visibility="public"
|
||||
* maxSize={5 * 1024 * 1024}
|
||||
* accept="image/*"
|
||||
* onSuccess={handleUpload}
|
||||
* onError={(err) => toast.error(err.message)}
|
||||
* />
|
||||
*/
|
||||
export function FileUpload({
|
||||
folder = 'files',
|
||||
visibility = 'private',
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import { HardDrive, Folder } from 'lucide-react';
|
||||
import { useStorageUsage } from '@/hooks/useStorage';
|
||||
|
||||
/**
|
||||
* Props for the StorageUsageCard component.
|
||||
*/
|
||||
interface StorageUsageCardProps {
|
||||
/** Additional CSS classes to apply to the card container */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@ -12,6 +16,24 @@ function formatBytes(bytes: number): string {
|
||||
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays storage usage statistics in a card format.
|
||||
* Shows total bytes used, usage percentage with color-coded progress bar,
|
||||
* file count, max file size, and breakdown by folder. Handles loading
|
||||
* and error states gracefully.
|
||||
*
|
||||
* @description Renders a card showing storage quota usage and file statistics.
|
||||
* @param props - The component props
|
||||
* @param props.className - Additional CSS classes for the card container
|
||||
*
|
||||
* @example
|
||||
* // Basic usage
|
||||
* <StorageUsageCard />
|
||||
*
|
||||
* @example
|
||||
* // With custom styling
|
||||
* <StorageUsageCard className="col-span-2 shadow-lg" />
|
||||
*/
|
||||
export function StorageUsageCard({ className = '' }: StorageUsageCardProps) {
|
||||
const { data, isLoading, error } = useStorageUsage();
|
||||
|
||||
|
||||
@ -14,15 +14,50 @@ import {
|
||||
} from 'lucide-react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/**
|
||||
* Props for the WebhookCard component.
|
||||
*/
|
||||
interface WebhookCardProps {
|
||||
/** The webhook data to display */
|
||||
webhook: Webhook;
|
||||
/** Callback invoked when the user clicks the edit action */
|
||||
onEdit: (webhook: Webhook) => void;
|
||||
/** Callback invoked when the user clicks the delete action */
|
||||
onDelete: (webhook: Webhook) => void;
|
||||
/** Callback invoked when the user clicks the test/send action */
|
||||
onTest: (webhook: Webhook) => void;
|
||||
/** Callback invoked when the user toggles the webhook active state */
|
||||
onToggle: (webhook: Webhook) => void;
|
||||
/** Callback invoked when the user wants to view delivery history */
|
||||
onViewDeliveries: (webhook: Webhook) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a single webhook configuration as a card with status, events, and statistics.
|
||||
*
|
||||
* @description Renders a card showing webhook details including name, URL, subscribed events,
|
||||
* delivery statistics, and a dropdown menu with actions (edit, test, view deliveries, toggle, delete).
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.webhook - The webhook data to display
|
||||
* @param props.onEdit - Callback when user clicks edit
|
||||
* @param props.onDelete - Callback when user clicks delete
|
||||
* @param props.onTest - Callback when user clicks send test
|
||||
* @param props.onToggle - Callback when user toggles active state
|
||||
* @param props.onViewDeliveries - Callback when user clicks view deliveries
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <WebhookCard
|
||||
* webhook={webhookData}
|
||||
* onEdit={(wh) => openEditModal(wh)}
|
||||
* onDelete={(wh) => confirmDelete(wh)}
|
||||
* onTest={(wh) => sendTestWebhook(wh)}
|
||||
* onToggle={(wh) => toggleWebhookStatus(wh)}
|
||||
* onViewDeliveries={(wh) => showDeliveryHistory(wh)}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function WebhookCard({
|
||||
webhook,
|
||||
onEdit,
|
||||
|
||||
@ -11,11 +11,19 @@ import {
|
||||
} from 'lucide-react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/**
|
||||
* Props for the WebhookDeliveryList component.
|
||||
*/
|
||||
interface WebhookDeliveryListProps {
|
||||
/** Array of webhook delivery records to display */
|
||||
deliveries: WebhookDelivery[];
|
||||
/** Whether the list is currently loading data */
|
||||
isLoading?: boolean;
|
||||
/** Callback invoked when the user requests to retry a failed delivery */
|
||||
onRetry: (deliveryId: string) => void;
|
||||
/** Callback invoked when the user requests to load more deliveries (pagination) */
|
||||
onLoadMore?: () => void;
|
||||
/** Indicates whether there are more deliveries available to load */
|
||||
hasMore?: boolean;
|
||||
}
|
||||
|
||||
@ -45,6 +53,16 @@ const statusConfig: Record<
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders a single delivery record as an expandable row.
|
||||
*
|
||||
* @description Displays delivery summary (event type, timestamp, status) with expand/collapse
|
||||
* functionality to show detailed information including payload, response, and retry options.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.delivery - The webhook delivery record to display
|
||||
* @param props.onRetry - Callback when user clicks retry on a failed delivery
|
||||
*/
|
||||
function DeliveryRow({
|
||||
delivery,
|
||||
onRetry,
|
||||
@ -183,6 +201,31 @@ function DeliveryRow({
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a paginated list of webhook delivery attempts with their status and details.
|
||||
*
|
||||
* @description Renders a list of delivery records showing status (delivered, failed, pending, retrying),
|
||||
* event type, timestamps, payload data, and response information. Supports pagination via "Load More"
|
||||
* and allows retrying failed deliveries.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.deliveries - Array of webhook delivery records to display
|
||||
* @param props.isLoading - Whether data is currently being loaded
|
||||
* @param props.onRetry - Callback when user requests to retry a failed delivery
|
||||
* @param props.onLoadMore - Optional callback to load more deliveries (pagination)
|
||||
* @param props.hasMore - Whether there are more deliveries available to load
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <WebhookDeliveryList
|
||||
* deliveries={webhookDeliveries}
|
||||
* isLoading={isLoadingDeliveries}
|
||||
* onRetry={(deliveryId) => retryDelivery(deliveryId)}
|
||||
* onLoadMore={() => fetchMoreDeliveries()}
|
||||
* hasMore={hasMoreDeliveries}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function WebhookDeliveryList({
|
||||
deliveries,
|
||||
isLoading,
|
||||
|
||||
@ -3,14 +3,58 @@ import { Webhook, CreateWebhookRequest, UpdateWebhookRequest, WebhookEvent } fro
|
||||
import { X, Plus, Trash2, Eye, EyeOff, Copy, Check } from 'lucide-react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/**
|
||||
* Props for the WebhookForm component.
|
||||
*/
|
||||
interface WebhookFormProps {
|
||||
/** Existing webhook data for editing, or null/undefined for creating a new webhook */
|
||||
webhook?: Webhook | null;
|
||||
/** List of available webhook events that can be subscribed to */
|
||||
events: WebhookEvent[];
|
||||
/** Callback invoked when the form is submitted with valid data */
|
||||
onSubmit: (data: CreateWebhookRequest | UpdateWebhookRequest) => void;
|
||||
/** Callback invoked when the user cancels the form */
|
||||
onCancel: () => void;
|
||||
/** Whether the form submission is in progress */
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form component for creating or editing webhook configurations.
|
||||
*
|
||||
* @description Provides a comprehensive form for webhook management including:
|
||||
* - Name and description fields
|
||||
* - HTTPS endpoint URL with validation
|
||||
* - Event selection checkboxes
|
||||
* - Custom HTTP headers management
|
||||
* - Signing secret display (edit mode only)
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.webhook - Existing webhook data for editing, or null for creating
|
||||
* @param props.events - List of available webhook events to subscribe to
|
||||
* @param props.onSubmit - Callback when form is submitted with valid data
|
||||
* @param props.onCancel - Callback when user cancels the form
|
||||
* @param props.isLoading - Whether submission is in progress (disables form)
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Creating a new webhook
|
||||
* <WebhookForm
|
||||
* events={availableEvents}
|
||||
* onSubmit={(data) => createWebhook(data)}
|
||||
* onCancel={() => closeModal()}
|
||||
* />
|
||||
*
|
||||
* // Editing an existing webhook
|
||||
* <WebhookForm
|
||||
* webhook={existingWebhook}
|
||||
* events={availableEvents}
|
||||
* onSubmit={(data) => updateWebhook(existingWebhook.id, data)}
|
||||
* onCancel={() => closeModal()}
|
||||
* isLoading={isSaving}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function WebhookForm({
|
||||
webhook,
|
||||
events,
|
||||
|
||||
@ -1,10 +1,46 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTestWhatsAppConnection } from '../../hooks/useWhatsApp';
|
||||
|
||||
/**
|
||||
* Props for the WhatsAppTestMessage component.
|
||||
*/
|
||||
interface WhatsAppTestMessageProps {
|
||||
/**
|
||||
* When true, disables the input field and submit button.
|
||||
* Useful when the WhatsApp integration is not configured.
|
||||
* @default false
|
||||
*/
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A form component for testing WhatsApp Business API connectivity.
|
||||
*
|
||||
* Allows users to send a test message to a specified phone number in E.164 format
|
||||
* to verify that the WhatsApp integration is working correctly.
|
||||
*
|
||||
* @description Renders a card with a phone number input and submit button.
|
||||
* Uses the useTestWhatsAppConnection hook to send the test message.
|
||||
* Shows loading state while the message is being sent.
|
||||
*
|
||||
* @param props - The component props
|
||||
* @param props.disabled - Optional flag to disable the form controls
|
||||
*
|
||||
* @example
|
||||
* // Basic usage
|
||||
* <WhatsAppTestMessage />
|
||||
*
|
||||
* @example
|
||||
* // Disabled state when integration is not configured
|
||||
* <WhatsAppTestMessage disabled={true} />
|
||||
*
|
||||
* @example
|
||||
* // Usage within a settings panel
|
||||
* <div className="settings-panel">
|
||||
* <WhatsAppConfig />
|
||||
* <WhatsAppTestMessage disabled={!isConfigured} />
|
||||
* </div>
|
||||
*/
|
||||
export function WhatsAppTestMessage({ disabled }: WhatsAppTestMessageProps) {
|
||||
const [phoneNumber, setPhoneNumber] = useState('');
|
||||
const testConnection = useTestWhatsAppConnection();
|
||||
|
||||
@ -30,6 +30,21 @@ import { TenantsPage, TenantDetailPage, MetricsPage } from '@/pages/superadmin';
|
||||
// Onboarding
|
||||
import { OnboardingPage } from '@/pages/onboarding';
|
||||
|
||||
// Sales pages
|
||||
import SalesPage from '@/pages/sales';
|
||||
import LeadsPage from '@/pages/sales/leads';
|
||||
import LeadDetailPage from '@/pages/sales/leads/[id]';
|
||||
import OpportunitiesPage from '@/pages/sales/opportunities';
|
||||
import OpportunityDetailPage from '@/pages/sales/opportunities/[id]';
|
||||
import ActivitiesPage from '@/pages/sales/activities';
|
||||
|
||||
// Commissions pages
|
||||
import CommissionsPage from '@/pages/commissions';
|
||||
import SchemesPage from '@/pages/commissions/schemes';
|
||||
import EntriesPage from '@/pages/commissions/entries';
|
||||
import PeriodsPage from '@/pages/commissions/periods';
|
||||
import MyEarningsPage from '@/pages/commissions/my-earnings';
|
||||
|
||||
// Protected Route wrapper
|
||||
function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
||||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
||||
@ -108,6 +123,21 @@ export function AppRouter() {
|
||||
<Route path="audit" element={<AuditLogsPage />} />
|
||||
<Route path="feature-flags" element={<FeatureFlagsPage />} />
|
||||
<Route path="whatsapp" element={<WhatsAppSettings />} />
|
||||
|
||||
{/* Sales routes */}
|
||||
<Route path="sales" element={<SalesPage />} />
|
||||
<Route path="sales/leads" element={<LeadsPage />} />
|
||||
<Route path="sales/leads/:id" element={<LeadDetailPage />} />
|
||||
<Route path="sales/opportunities" element={<OpportunitiesPage />} />
|
||||
<Route path="sales/opportunities/:id" element={<OpportunityDetailPage />} />
|
||||
<Route path="sales/activities" element={<ActivitiesPage />} />
|
||||
|
||||
{/* Commissions routes */}
|
||||
<Route path="commissions" element={<CommissionsPage />} />
|
||||
<Route path="commissions/schemes" element={<SchemesPage />} />
|
||||
<Route path="commissions/entries" element={<EntriesPage />} />
|
||||
<Route path="commissions/periods" element={<PeriodsPage />} />
|
||||
<Route path="commissions/my-earnings" element={<MyEarningsPage />} />
|
||||
</Route>
|
||||
|
||||
{/* Superadmin routes */}
|
||||
|
||||
@ -10,6 +10,11 @@ export interface User {
|
||||
tenant_id: string;
|
||||
}
|
||||
|
||||
export interface UpdateProfileData {
|
||||
first_name?: string;
|
||||
last_name?: string;
|
||||
}
|
||||
|
||||
export interface AuthState {
|
||||
user: User | null;
|
||||
accessToken: string | null;
|
||||
@ -23,6 +28,8 @@ export interface AuthState {
|
||||
login: (user: User, accessToken: string, refreshToken: string) => void;
|
||||
logout: () => void;
|
||||
setLoading: (loading: boolean) => void;
|
||||
refreshTokens: () => Promise<boolean>;
|
||||
updateProfile: (data: UpdateProfileData) => void;
|
||||
}
|
||||
|
||||
export const useAuthStore = create<AuthState>()(
|
||||
@ -57,6 +64,57 @@ export const useAuthStore = create<AuthState>()(
|
||||
}),
|
||||
|
||||
setLoading: (isLoading) => set({ isLoading }),
|
||||
|
||||
refreshTokens: async () => {
|
||||
const state = useAuthStore.getState();
|
||||
if (!state.refreshToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${import.meta.env.VITE_API_URL || '/api/v1'}/auth/refresh`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ refreshToken: state.refreshToken }),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to refresh token');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
set({
|
||||
accessToken: data.accessToken,
|
||||
refreshToken: data.refreshToken,
|
||||
});
|
||||
return true;
|
||||
} catch {
|
||||
set({
|
||||
user: null,
|
||||
accessToken: null,
|
||||
refreshToken: null,
|
||||
isAuthenticated: false,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
updateProfile: (data) => {
|
||||
set((state) => ({
|
||||
user: state.user
|
||||
? {
|
||||
...state.user,
|
||||
...(data.first_name !== undefined && { first_name: data.first_name }),
|
||||
...(data.last_name !== undefined && { last_name: data.last_name }),
|
||||
}
|
||||
: null,
|
||||
}));
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'auth-storage',
|
||||
|
||||
804
docs/05-frontend/FRONTEND-PAGES-SPEC.md
Normal file
804
docs/05-frontend/FRONTEND-PAGES-SPEC.md
Normal file
@ -0,0 +1,804 @@
|
||||
# Especificación de Páginas Frontend - Template SaaS
|
||||
|
||||
**Versión:** 1.0.0
|
||||
**Actualizado:** 2026-01-25
|
||||
**Total Páginas:** 38
|
||||
|
||||
---
|
||||
|
||||
## Índice
|
||||
|
||||
1. [Auth Pages](#1-auth-pages)
|
||||
2. [Dashboard Pages](#2-dashboard-pages)
|
||||
3. [Sales Pages](#3-sales-pages)
|
||||
4. [Commissions Pages](#4-commissions-pages)
|
||||
5. [Settings Pages](#5-settings-pages)
|
||||
6. [Superadmin Pages](#6-superadmin-pages)
|
||||
7. [Onboarding Pages](#7-onboarding-pages)
|
||||
|
||||
---
|
||||
|
||||
## 1. Auth Pages
|
||||
|
||||
### 1.1 LoginPage
|
||||
|
||||
**Ruta:** `/auth/login`
|
||||
**Archivo:** `src/pages/auth/LoginPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Autenticación de usuarios existentes |
|
||||
| **Layout** | AuthLayout |
|
||||
| **Guard** | GuestRoute |
|
||||
| **Componentes** | LoginForm, OAuthButtons |
|
||||
| **Hooks** | useAuth, useForm |
|
||||
| **APIs** | `authApi.login()` |
|
||||
|
||||
**Estados:**
|
||||
- `idle` - Formulario disponible
|
||||
- `loading` - Enviando credenciales
|
||||
- `error` - Credenciales inválidas
|
||||
- `success` - Redirige a `/dashboard`
|
||||
|
||||
**Campos:**
|
||||
- Email (required, email format)
|
||||
- Password (required, min 8 chars)
|
||||
- Remember me (checkbox)
|
||||
|
||||
---
|
||||
|
||||
### 1.2 RegisterPage
|
||||
|
||||
**Ruta:** `/auth/register`
|
||||
**Archivo:** `src/pages/auth/RegisterPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Registro de nuevos usuarios |
|
||||
| **Layout** | AuthLayout |
|
||||
| **Guard** | GuestRoute |
|
||||
| **Componentes** | RegisterForm, OAuthButtons |
|
||||
| **Hooks** | useAuth, useForm |
|
||||
| **APIs** | `authApi.register()` |
|
||||
|
||||
**Estados:**
|
||||
- `idle` - Formulario disponible
|
||||
- `loading` - Creando cuenta
|
||||
- `error` - Email duplicado u otro error
|
||||
- `success` - Redirige a `/onboarding`
|
||||
|
||||
**Campos:**
|
||||
- First name (optional)
|
||||
- Last name (optional)
|
||||
- Email (required, email format)
|
||||
- Password (required, min 8 chars, complexity)
|
||||
- Confirm password (must match)
|
||||
- Terms acceptance (required)
|
||||
|
||||
---
|
||||
|
||||
### 1.3 ForgotPasswordPage
|
||||
|
||||
**Ruta:** `/auth/forgot-password`
|
||||
**Archivo:** `src/pages/auth/ForgotPasswordPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Solicitar reset de contraseña |
|
||||
| **Layout** | AuthLayout |
|
||||
| **Guard** | GuestRoute |
|
||||
| **Componentes** | Form básico |
|
||||
| **Hooks** | useForm |
|
||||
| **APIs** | `authApi.requestPasswordReset()` |
|
||||
|
||||
**Estados:**
|
||||
- `idle` - Formulario disponible
|
||||
- `loading` - Enviando solicitud
|
||||
- `success` - Muestra mensaje de email enviado
|
||||
- `error` - Email no encontrado
|
||||
|
||||
---
|
||||
|
||||
## 2. Dashboard Pages
|
||||
|
||||
### 2.1 DashboardPage
|
||||
|
||||
**Ruta:** `/dashboard`
|
||||
**Archivo:** `src/pages/dashboard/DashboardPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Vista principal con métricas y acciones rápidas |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | MetricCard, TrendChart, QuickActions |
|
||||
| **Hooks** | useAuth, useSubscription, useNotifications |
|
||||
| **APIs** | `billingApi.getSummary()`, `notificationsApi.getUnreadCount()` |
|
||||
|
||||
**Secciones:**
|
||||
- Welcome banner con nombre de usuario
|
||||
- Métricas principales (usuarios, storage, API calls)
|
||||
- Gráfico de actividad reciente
|
||||
- Acciones rápidas
|
||||
- Notificaciones recientes
|
||||
|
||||
---
|
||||
|
||||
### 2.2 BillingPage
|
||||
|
||||
**Ruta:** `/dashboard/billing`
|
||||
**Archivo:** `src/pages/dashboard/BillingPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Gestión de suscripción y pagos |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | SubscriptionCard, InvoiceList, PaymentMethodCard |
|
||||
| **Hooks** | useSubscription, useInvoices, useBillingPortal |
|
||||
| **APIs** | `billingApi.*`, `stripeApi.*` |
|
||||
|
||||
**Secciones:**
|
||||
- Plan actual y estado
|
||||
- Uso del mes (límites)
|
||||
- Métodos de pago
|
||||
- Historial de facturas
|
||||
- Botón "Manage Subscription" (Stripe Portal)
|
||||
|
||||
---
|
||||
|
||||
### 2.3 UsersPage
|
||||
|
||||
**Ruta:** `/dashboard/users`
|
||||
**Archivo:** `src/pages/dashboard/UsersPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Gestión de usuarios del tenant |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | UserTable, InviteModal, RoleSelect |
|
||||
| **Hooks** | useUsers, useRoles |
|
||||
| **APIs** | `usersApi.list()`, `usersApi.invite()`, `usersApi.update()` |
|
||||
|
||||
**Acciones:**
|
||||
- Listar usuarios
|
||||
- Invitar nuevos usuarios
|
||||
- Cambiar roles
|
||||
- Desactivar usuarios
|
||||
|
||||
---
|
||||
|
||||
### 2.4 AIPage
|
||||
|
||||
**Ruta:** `/dashboard/ai`
|
||||
**Archivo:** `src/pages/dashboard/AIPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Chat con LLM y configuración |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | AIChat, AISettings, ChatMessage |
|
||||
| **Hooks** | useAI, useAIChat, useAIUsage |
|
||||
| **APIs** | `aiApi.chat()`, `aiApi.getConfig()`, `aiApi.getCurrentUsage()` |
|
||||
|
||||
**Secciones:**
|
||||
- Chat interface con historial
|
||||
- Selector de modelo
|
||||
- Uso actual (tokens, costo)
|
||||
- Configuración (temperatura, max tokens)
|
||||
|
||||
---
|
||||
|
||||
### 2.5 StoragePage
|
||||
|
||||
**Ruta:** `/dashboard/storage`
|
||||
**Archivo:** `src/pages/dashboard/StoragePage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Gestión de archivos |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | FileList, FileUpload, FileItem, StorageUsageCard |
|
||||
| **Hooks** | useFiles, useUpload, useStorageUsage |
|
||||
| **APIs** | `storageApi.listFiles()`, `storageApi.uploadFile()`, `storageApi.deleteFile()` |
|
||||
|
||||
**Acciones:**
|
||||
- Subir archivos (drag & drop)
|
||||
- Listar archivos con paginación
|
||||
- Filtrar por carpeta/tipo
|
||||
- Descargar archivos
|
||||
- Eliminar archivos
|
||||
- Ver uso de storage
|
||||
|
||||
---
|
||||
|
||||
### 2.6 WebhooksPage
|
||||
|
||||
**Ruta:** `/dashboard/webhooks`
|
||||
**Archivo:** `src/pages/dashboard/WebhooksPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Configuración de webhooks outbound |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | WebhookCard, WebhookForm, WebhookDeliveryList |
|
||||
| **Hooks** | useWebhooks, useWebhookDeliveries |
|
||||
| **APIs** | `webhooksApi.*` |
|
||||
|
||||
**Acciones:**
|
||||
- Crear webhook
|
||||
- Editar URL, eventos, headers
|
||||
- Ver historial de entregas
|
||||
- Reintentar entregas fallidas
|
||||
- Test webhook
|
||||
|
||||
---
|
||||
|
||||
### 2.7 AuditLogsPage
|
||||
|
||||
**Ruta:** `/dashboard/audit`
|
||||
**Archivo:** `src/pages/dashboard/AuditLogsPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Registro de auditoría de acciones |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | AuditLogRow, AuditFilters, AuditStatsCard, ActivityTimeline |
|
||||
| **Hooks** | useAuditLogs, useActivityLogs |
|
||||
| **APIs** | `auditApi.queryLogs()`, `auditApi.getStats()` |
|
||||
|
||||
**Filtros:**
|
||||
- Por usuario
|
||||
- Por acción (create, update, delete, login)
|
||||
- Por entidad
|
||||
- Por fecha
|
||||
|
||||
---
|
||||
|
||||
### 2.8 FeatureFlagsPage
|
||||
|
||||
**Ruta:** `/dashboard/feature-flags`
|
||||
**Archivo:** `src/pages/dashboard/FeatureFlagsPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Gestión de feature toggles |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | FeatureFlagCard, FeatureFlagForm, TenantOverridesPanel |
|
||||
| **Hooks** | useFeatureFlags, useFeatureFlag |
|
||||
| **APIs** | `featureFlagsApi.*` |
|
||||
|
||||
**Acciones:**
|
||||
- Listar flags
|
||||
- Crear/editar flags
|
||||
- Toggle on/off
|
||||
- Configurar overrides por tenant/usuario
|
||||
- Ver targeting rules
|
||||
|
||||
---
|
||||
|
||||
### 2.9 WhatsAppSettings
|
||||
|
||||
**Ruta:** `/dashboard/whatsapp`
|
||||
**Archivo:** `src/pages/admin/WhatsAppSettings.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Configuración WhatsApp Business API |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | WhatsAppTestMessage |
|
||||
| **Hooks** | useWhatsApp |
|
||||
| **APIs** | `whatsappApi.*` |
|
||||
|
||||
**Secciones:**
|
||||
- Estado de conexión
|
||||
- Configuración de API
|
||||
- Envío de mensaje de prueba
|
||||
- Templates de mensajes
|
||||
|
||||
---
|
||||
|
||||
## 3. Sales Pages
|
||||
|
||||
### 3.1 SalesPage (Dashboard)
|
||||
|
||||
**Ruta:** `/dashboard/sales`
|
||||
**Archivo:** `src/pages/sales/index.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Dashboard de ventas con métricas |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | SalesDashboard |
|
||||
| **Hooks** | useSalesSummary, useConversionRates |
|
||||
| **APIs** | Sales Dashboard API |
|
||||
|
||||
**Métricas:**
|
||||
- Total leads
|
||||
- Conversion rate
|
||||
- Pipeline value
|
||||
- Deals cerrados
|
||||
|
||||
---
|
||||
|
||||
### 3.2 LeadsPage
|
||||
|
||||
**Ruta:** `/dashboard/sales/leads`
|
||||
**Archivo:** `src/pages/sales/leads/index.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Listado y gestión de leads |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | LeadsList, LeadForm |
|
||||
| **Hooks** | useLeads, useLeadStats |
|
||||
| **APIs** | Leads API |
|
||||
|
||||
**Filtros:**
|
||||
- Search (nombre, email, empresa)
|
||||
- Status (new, contacted, qualified, unqualified, converted)
|
||||
- Source (website, referral, cold_call, event, advertisement, social_media)
|
||||
|
||||
**Acciones:**
|
||||
- Crear lead
|
||||
- Ver detalle
|
||||
- Filtrar y buscar
|
||||
|
||||
---
|
||||
|
||||
### 3.3 LeadDetailPage
|
||||
|
||||
**Ruta:** `/dashboard/sales/leads/:id`
|
||||
**Archivo:** `src/pages/sales/leads/[id].tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Detalle y edición de un lead |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | LeadCard, LeadForm, ActivityTimeline, ActivityForm |
|
||||
| **Hooks** | useLead, useDeleteLead, useConvertLead, useLeadActivities |
|
||||
| **APIs** | Leads API, Activities API |
|
||||
|
||||
**Secciones:**
|
||||
- Información del lead
|
||||
- Score
|
||||
- Información de contacto
|
||||
- Timeline de actividades
|
||||
- Notas
|
||||
|
||||
**Acciones:**
|
||||
- Editar lead
|
||||
- Eliminar lead
|
||||
- Convertir a oportunidad
|
||||
- Agregar actividad
|
||||
|
||||
---
|
||||
|
||||
### 3.4 OpportunitiesPage
|
||||
|
||||
**Ruta:** `/dashboard/sales/opportunities`
|
||||
**Archivo:** `src/pages/sales/opportunities/index.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Pipeline de oportunidades (Kanban) |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | PipelineBoard, OpportunityForm |
|
||||
| **Hooks** | usePipeline, useOpportunityStats |
|
||||
| **APIs** | Pipeline API, Opportunities API |
|
||||
|
||||
**Vistas:**
|
||||
- Pipeline (Kanban)
|
||||
- List view
|
||||
|
||||
**Métricas:**
|
||||
- Open
|
||||
- Won
|
||||
- Win Rate
|
||||
- Avg Deal Size
|
||||
|
||||
---
|
||||
|
||||
### 3.5 OpportunityDetailPage
|
||||
|
||||
**Ruta:** `/dashboard/sales/opportunities/:id`
|
||||
**Archivo:** `src/pages/sales/opportunities/[id].tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Detalle y gestión de una oportunidad |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | OpportunityForm, ActivityTimeline, ActivityForm |
|
||||
| **Hooks** | useOpportunity, useDeleteOpportunity, useMarkAsWon, useMarkAsLost, useOpportunityActivities |
|
||||
| **APIs** | Opportunities API, Activities API |
|
||||
|
||||
**Secciones:**
|
||||
- Valor y probabilidad
|
||||
- Descripción
|
||||
- Timeline de actividades
|
||||
- Status (Open/Won/Lost)
|
||||
- Contacto
|
||||
- Timeline de fechas
|
||||
|
||||
**Acciones:**
|
||||
- Editar oportunidad
|
||||
- Eliminar
|
||||
- Marcar como Won
|
||||
- Marcar como Lost
|
||||
- Agregar actividad
|
||||
|
||||
---
|
||||
|
||||
### 3.6 ActivitiesPage
|
||||
|
||||
**Ruta:** `/dashboard/sales/activities`
|
||||
**Archivo:** `src/pages/sales/activities/index.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Gestión de actividades de ventas |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | ActivityForm |
|
||||
| **Hooks** | useActivities, useUpcomingActivities, useOverdueActivities, useActivityStats |
|
||||
| **APIs** | Activities API |
|
||||
|
||||
**Tipos de actividad:**
|
||||
- Call
|
||||
- Meeting
|
||||
- Task
|
||||
- Email
|
||||
- Note
|
||||
|
||||
**Estados:**
|
||||
- Pending
|
||||
- Completed
|
||||
- Cancelled
|
||||
|
||||
---
|
||||
|
||||
## 4. Commissions Pages
|
||||
|
||||
### 4.1 CommissionsPage (Dashboard)
|
||||
|
||||
**Ruta:** `/dashboard/commissions`
|
||||
**Archivo:** `src/pages/commissions/index.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Dashboard de comisiones |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | CommissionsDashboard |
|
||||
| **Hooks** | useCommissionsSummary, useTopEarners, useEarningsByPeriod |
|
||||
| **APIs** | Commissions Dashboard API |
|
||||
|
||||
**Métricas:**
|
||||
- Total comisiones
|
||||
- Pendientes
|
||||
- Aprobadas
|
||||
- Top earners
|
||||
|
||||
---
|
||||
|
||||
### 4.2 SchemesPage
|
||||
|
||||
**Ruta:** `/dashboard/commissions/schemes`
|
||||
**Archivo:** `src/pages/commissions/schemes/index.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Configuración de esquemas de comisiones |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | SchemesList, SchemeForm |
|
||||
| **Hooks** | useSchemes |
|
||||
| **APIs** | Schemes API |
|
||||
|
||||
**Tipos de esquema:**
|
||||
- Percentage
|
||||
- Fixed
|
||||
- Tiered
|
||||
|
||||
**Filtros:**
|
||||
- Type
|
||||
- Status (active/inactive)
|
||||
- Search
|
||||
|
||||
---
|
||||
|
||||
### 4.3 EntriesPage
|
||||
|
||||
**Ruta:** `/dashboard/commissions/entries`
|
||||
**Archivo:** `src/pages/commissions/entries/index.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Gestión de entradas de comisiones |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | EntriesList |
|
||||
| **Hooks** | useEntries, useBulkApprove, useBulkReject |
|
||||
| **APIs** | Entries API |
|
||||
|
||||
**Estados:**
|
||||
- Pending
|
||||
- Approved
|
||||
- Rejected
|
||||
- Paid
|
||||
- Cancelled
|
||||
|
||||
**Acciones bulk:**
|
||||
- Aprobar seleccionados
|
||||
- Rechazar seleccionados
|
||||
|
||||
---
|
||||
|
||||
### 4.4 PeriodsPage
|
||||
|
||||
**Ruta:** `/dashboard/commissions/periods`
|
||||
**Archivo:** `src/pages/commissions/periods/index.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Gestión de períodos de pago |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | PeriodManager |
|
||||
| **Hooks** | usePeriods, useCreatePeriod |
|
||||
| **APIs** | Periods API |
|
||||
|
||||
**Estados de período:**
|
||||
- Open
|
||||
- Closed
|
||||
- Processing
|
||||
- Paid
|
||||
|
||||
---
|
||||
|
||||
### 4.5 MyEarningsPage
|
||||
|
||||
**Ruta:** `/dashboard/commissions/my-earnings`
|
||||
**Archivo:** `src/pages/commissions/my-earnings/index.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Vista de ganancias del usuario actual |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | EarningsCard, EntryStatusBadge |
|
||||
| **Hooks** | useMyEarnings |
|
||||
| **APIs** | Dashboard API (my earnings) |
|
||||
|
||||
**Secciones:**
|
||||
- Total earnings
|
||||
- Pending
|
||||
- Approved
|
||||
- Current period earnings
|
||||
- Recent commissions table
|
||||
|
||||
---
|
||||
|
||||
## 5. Settings Pages
|
||||
|
||||
### 5.1 SettingsPage
|
||||
|
||||
**Ruta:** `/dashboard/settings`
|
||||
**Archivo:** `src/pages/settings/index.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Configuración de cuenta (tabs) |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | GeneralSettings, NotificationSettings, SecuritySettings |
|
||||
| **Hooks** | useAuth, useCurrentUser |
|
||||
| **APIs** | `usersApi.update()`, `notificationsApi.*`, `authApi.*` |
|
||||
|
||||
**Tabs:**
|
||||
- General (perfil)
|
||||
- Notifications (preferencias)
|
||||
- Security (password, MFA)
|
||||
- AI (configuración AI)
|
||||
|
||||
---
|
||||
|
||||
### 5.2 GeneralSettings
|
||||
|
||||
**Archivo:** `src/pages/settings/GeneralSettings.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Configuración de perfil |
|
||||
| **Componentes** | Form básico |
|
||||
| **Hooks** | useCurrentUser, useForm |
|
||||
|
||||
**Campos:**
|
||||
- First name
|
||||
- Last name
|
||||
- Email (read-only)
|
||||
- Avatar
|
||||
|
||||
---
|
||||
|
||||
### 5.3 NotificationSettings
|
||||
|
||||
**Archivo:** `src/pages/settings/NotificationSettings.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Preferencias de notificaciones |
|
||||
| **Componentes** | Toggle switches |
|
||||
| **Hooks** | useNotifications |
|
||||
|
||||
**Configuración:**
|
||||
- Email notifications
|
||||
- Push notifications
|
||||
- In-app notifications
|
||||
- Tipos de eventos
|
||||
|
||||
---
|
||||
|
||||
### 5.4 SecuritySettings
|
||||
|
||||
**Archivo:** `src/pages/settings/SecuritySettings.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Seguridad de la cuenta |
|
||||
| **Componentes** | Form de password |
|
||||
| **Hooks** | useAuth |
|
||||
|
||||
**Secciones:**
|
||||
- Cambiar contraseña
|
||||
- Sesiones activas
|
||||
- MFA (futuro)
|
||||
|
||||
---
|
||||
|
||||
## 6. Superadmin Pages
|
||||
|
||||
### 6.1 TenantsPage
|
||||
|
||||
**Ruta:** `/superadmin/tenants`
|
||||
**Archivo:** `src/pages/superadmin/TenantsPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Listado de todos los tenants |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | SuperadminRoute |
|
||||
| **Componentes** | TenantTable |
|
||||
| **Hooks** | useSuperadmin |
|
||||
| **APIs** | `superadminApi.listTenants()` |
|
||||
|
||||
**Filtros:**
|
||||
- Search
|
||||
- Status
|
||||
- Plan
|
||||
|
||||
---
|
||||
|
||||
### 6.2 TenantDetailPage
|
||||
|
||||
**Ruta:** `/superadmin/tenants/:id`
|
||||
**Archivo:** `src/pages/superadmin/TenantDetailPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Detalle de un tenant específico |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | SuperadminRoute |
|
||||
| **Componentes** | TenantForm, UserTable |
|
||||
| **Hooks** | useSuperadmin |
|
||||
| **APIs** | `superadminApi.getTenant()`, `superadminApi.getTenantUsers()` |
|
||||
|
||||
**Secciones:**
|
||||
- Información del tenant
|
||||
- Usuarios del tenant
|
||||
- Suscripción
|
||||
- Métricas de uso
|
||||
|
||||
---
|
||||
|
||||
### 6.3 MetricsPage
|
||||
|
||||
**Ruta:** `/superadmin/metrics`
|
||||
**Archivo:** `src/pages/superadmin/MetricsPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Métricas globales del sistema |
|
||||
| **Layout** | DashboardLayout |
|
||||
| **Guard** | SuperadminRoute |
|
||||
| **Componentes** | MetricCard, TrendChart |
|
||||
| **Hooks** | useSuperadmin |
|
||||
| **APIs** | `superadminApi.getMetricsSummary()`, `superadminApi.getTenantGrowth()` |
|
||||
|
||||
**Métricas:**
|
||||
- Total tenants
|
||||
- Total users
|
||||
- MRR
|
||||
- Tenant growth chart
|
||||
- Plan distribution
|
||||
- Top tenants
|
||||
|
||||
---
|
||||
|
||||
## 7. Onboarding Pages
|
||||
|
||||
### 7.1 OnboardingPage
|
||||
|
||||
**Ruta:** `/onboarding`
|
||||
**Archivo:** `src/pages/onboarding/OnboardingPage.tsx`
|
||||
|
||||
| Aspecto | Detalle |
|
||||
|---------|---------|
|
||||
| **Propósito** | Wizard de onboarding para nuevos tenants |
|
||||
| **Layout** | Propio (sin DashboardLayout) |
|
||||
| **Guard** | ProtectedRoute |
|
||||
| **Componentes** | WizardProgress, CompanyStep, PlanStep, InviteStep, CompleteStep |
|
||||
| **Hooks** | useOnboarding, useOnboardingStatus |
|
||||
| **APIs** | Onboarding API |
|
||||
|
||||
**Pasos:**
|
||||
1. **CompanyStep** - Información de la empresa (nombre, logo)
|
||||
2. **PlanStep** - Selección de plan
|
||||
3. **InviteStep** - Invitar miembros del equipo
|
||||
4. **CompleteStep** - Confirmación y redirección
|
||||
|
||||
---
|
||||
|
||||
## Resumen de Estados Comunes
|
||||
|
||||
### Loading State
|
||||
```tsx
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600" />
|
||||
</div>
|
||||
```
|
||||
|
||||
### Empty State
|
||||
```tsx
|
||||
<div className="text-center py-12">
|
||||
<p className="text-gray-500">No data found</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Error State
|
||||
```tsx
|
||||
<div className="bg-red-50 p-4 rounded-lg">
|
||||
<p className="text-red-600">{error.message}</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Resumen Cuantitativo
|
||||
|
||||
| Categoría | Cantidad |
|
||||
|-----------|----------|
|
||||
| Auth Pages | 3 |
|
||||
| Dashboard Core | 9 |
|
||||
| Sales Pages | 6 |
|
||||
| Commissions Pages | 5 |
|
||||
| Settings Pages | 4 |
|
||||
| Superadmin Pages | 3 |
|
||||
| Onboarding Pages | 1 (+4 steps) |
|
||||
| **Total** | **38** |
|
||||
|
||||
---
|
||||
|
||||
*Documentación generada - Template SaaS v1.0.0 - 2026-01-25*
|
||||
287
docs/05-frontend/FRONTEND-ROUTING.md
Normal file
287
docs/05-frontend/FRONTEND-ROUTING.md
Normal file
@ -0,0 +1,287 @@
|
||||
# Frontend Routing - Template SaaS
|
||||
|
||||
**Versión:** 1.0.0
|
||||
**Actualizado:** 2026-01-25
|
||||
**Router:** React Router DOM v7.1.1
|
||||
|
||||
---
|
||||
|
||||
## Arquitectura de Routing
|
||||
|
||||
El sistema de routing está basado en **React Router v6/7** con guards de protección para controlar el acceso a las rutas según el estado de autenticación y rol del usuario.
|
||||
|
||||
### Archivo Principal
|
||||
`apps/frontend/src/router/index.tsx`
|
||||
|
||||
---
|
||||
|
||||
## Guards de Protección
|
||||
|
||||
| Guard | Descripción | Redirección |
|
||||
|-------|-------------|-------------|
|
||||
| **GuestRoute** | Solo usuarios no autenticados | → `/dashboard` si está autenticado |
|
||||
| **ProtectedRoute** | Requiere autenticación | → `/auth/login` si no está autenticado |
|
||||
| **SuperadminRoute** | Requiere rol `superadmin` | → `/dashboard` si no tiene rol |
|
||||
|
||||
---
|
||||
|
||||
## Mapa de Rutas Completo
|
||||
|
||||
### 1. Rutas Públicas
|
||||
|
||||
| Ruta | Componente | Guard | Descripción |
|
||||
|------|------------|-------|-------------|
|
||||
| `/` | Navigate | - | Redirect a `/auth/login` |
|
||||
|
||||
### 2. Rutas de Autenticación (`/auth`)
|
||||
|
||||
**Layout:** `AuthLayout`
|
||||
**Guard:** `GuestRoute`
|
||||
|
||||
| Ruta | Componente | Descripción |
|
||||
|------|------------|-------------|
|
||||
| `/auth/login` | `LoginPage` | Formulario de inicio de sesión |
|
||||
| `/auth/register` | `RegisterPage` | Formulario de registro |
|
||||
| `/auth/forgot-password` | `ForgotPasswordPage` | Recuperación de contraseña |
|
||||
|
||||
### 3. Rutas de Dashboard (`/dashboard`)
|
||||
|
||||
**Layout:** `DashboardLayout`
|
||||
**Guard:** `ProtectedRoute`
|
||||
|
||||
#### 3.1 Rutas Core
|
||||
|
||||
| Ruta | Componente | Descripción |
|
||||
|------|------------|-------------|
|
||||
| `/dashboard` | `DashboardPage` | Dashboard principal con métricas |
|
||||
| `/dashboard/settings` | `SettingsPage` | Configuración de cuenta |
|
||||
| `/dashboard/billing` | `BillingPage` | Gestión de suscripción (Stripe) |
|
||||
| `/dashboard/users` | `UsersPage` | Gestión de usuarios del tenant |
|
||||
| `/dashboard/ai` | `AIPage` | Panel de control AI/LLM |
|
||||
| `/dashboard/storage` | `StoragePage` | Gestión de archivos |
|
||||
| `/dashboard/webhooks` | `WebhooksPage` | Configuración de webhooks |
|
||||
| `/dashboard/audit` | `AuditLogsPage` | Registro de auditoría |
|
||||
| `/dashboard/feature-flags` | `FeatureFlagsPage` | Feature toggles |
|
||||
| `/dashboard/whatsapp` | `WhatsAppSettings` | Config WhatsApp Business |
|
||||
|
||||
#### 3.2 Rutas de Sales (`/dashboard/sales`)
|
||||
|
||||
| Ruta | Componente | Parámetros | Descripción |
|
||||
|------|------------|------------|-------------|
|
||||
| `/dashboard/sales` | `SalesPage` | - | Dashboard de ventas |
|
||||
| `/dashboard/sales/leads` | `LeadsPage` | - | Listado de leads |
|
||||
| `/dashboard/sales/leads/:id` | `LeadDetailPage` | `id: string` | Detalle de lead |
|
||||
| `/dashboard/sales/opportunities` | `OpportunitiesPage` | - | Pipeline de oportunidades |
|
||||
| `/dashboard/sales/opportunities/:id` | `OpportunityDetailPage` | `id: string` | Detalle de oportunidad |
|
||||
| `/dashboard/sales/activities` | `ActivitiesPage` | - | Actividades de ventas |
|
||||
|
||||
#### 3.3 Rutas de Commissions (`/dashboard/commissions`)
|
||||
|
||||
| Ruta | Componente | Parámetros | Descripción |
|
||||
|------|------------|------------|-------------|
|
||||
| `/dashboard/commissions` | `CommissionsPage` | - | Dashboard de comisiones |
|
||||
| `/dashboard/commissions/schemes` | `SchemesPage` | - | Esquemas de comisiones |
|
||||
| `/dashboard/commissions/entries` | `EntriesPage` | - | Entradas de comisiones |
|
||||
| `/dashboard/commissions/periods` | `PeriodsPage` | - | Períodos de pago |
|
||||
| `/dashboard/commissions/my-earnings` | `MyEarningsPage` | - | Mis ganancias |
|
||||
|
||||
### 4. Rutas de Superadmin (`/superadmin`)
|
||||
|
||||
**Layout:** `DashboardLayout`
|
||||
**Guard:** `SuperadminRoute`
|
||||
|
||||
| Ruta | Componente | Parámetros | Descripción |
|
||||
|------|------------|------------|-------------|
|
||||
| `/superadmin` | Navigate | - | Redirect a `/superadmin/tenants` |
|
||||
| `/superadmin/tenants` | `TenantsPage` | - | Listado de tenants |
|
||||
| `/superadmin/tenants/:id` | `TenantDetailPage` | `id: string` | Detalle de tenant |
|
||||
| `/superadmin/metrics` | `MetricsPage` | - | Métricas del sistema |
|
||||
|
||||
### 5. Ruta de Onboarding
|
||||
|
||||
| Ruta | Componente | Guard | Descripción |
|
||||
|------|------------|-------|-------------|
|
||||
| `/onboarding` | `OnboardingPage` | `ProtectedRoute` | Wizard de onboarding (4 pasos) |
|
||||
|
||||
### 6. Ruta 404 (Catch-All)
|
||||
|
||||
| Ruta | Componente | Descripción |
|
||||
|------|------------|-------------|
|
||||
| `*` | Navigate | Redirect a `/` |
|
||||
|
||||
---
|
||||
|
||||
## Diagrama de Navegación
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ / │
|
||||
│ (redirect) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────┴──────────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ /auth/* │ │ /dashboard/* │
|
||||
│ (GuestRoute) │ │ (ProtectedRoute)│
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ login │◄─────────────────►│ (index) │
|
||||
│ register │ login/logout │ settings │
|
||||
│ forgot-password │ │ billing │
|
||||
└─────────────────┘ │ users │
|
||||
│ ai │
|
||||
│ storage │
|
||||
│ webhooks │
|
||||
│ audit │
|
||||
│ feature-flags │
|
||||
│ whatsapp │
|
||||
│ │
|
||||
│ sales/* │
|
||||
│ ├─ (index) │
|
||||
│ ├─ leads │
|
||||
│ ├─ leads/:id │
|
||||
│ ├─ opportunities│
|
||||
│ ├─ opportunities/:id │
|
||||
│ └─ activities │
|
||||
│ │
|
||||
│ commissions/* │
|
||||
│ ├─ (index) │
|
||||
│ ├─ schemes │
|
||||
│ ├─ entries │
|
||||
│ ├─ periods │
|
||||
│ └─ my-earnings │
|
||||
└────────┬────────┘
|
||||
│
|
||||
│ (superadmin only)
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ /superadmin/* │
|
||||
│(SuperadminRoute)│
|
||||
├─────────────────┤
|
||||
│ tenants │
|
||||
│ tenants/:id │
|
||||
│ metrics │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Layouts
|
||||
|
||||
### AuthLayout
|
||||
- **Ubicación:** `src/layouts/AuthLayout.tsx`
|
||||
- **Uso:** Rutas de autenticación (`/auth/*`)
|
||||
- **Características:**
|
||||
- Diseño de 2 columnas (branding + formulario)
|
||||
- Responsive para móvil
|
||||
- Sin sidebar ni navegación
|
||||
|
||||
### DashboardLayout
|
||||
- **Ubicación:** `src/layouts/DashboardLayout.tsx`
|
||||
- **Uso:** Dashboard y Superadmin
|
||||
- **Características:**
|
||||
- Sidebar fijo con navegación
|
||||
- Top bar con usuario y notificaciones
|
||||
- Contenido principal con scroll
|
||||
- Menú dinámico según rol
|
||||
- Outlet para rutas hijas
|
||||
|
||||
---
|
||||
|
||||
## Flujos de Navegación
|
||||
|
||||
### Flujo de Autenticación
|
||||
```
|
||||
Usuario no autenticado
|
||||
│
|
||||
├─► /dashboard → redirect → /auth/login
|
||||
│
|
||||
└─► /auth/login → login exitoso → /dashboard
|
||||
```
|
||||
|
||||
### Flujo de Onboarding
|
||||
```
|
||||
Nuevo usuario (post-registro)
|
||||
│
|
||||
└─► /onboarding
|
||||
│
|
||||
├─ Step 1: CompanyStep (info empresa)
|
||||
├─ Step 2: PlanStep (selección plan)
|
||||
├─ Step 3: InviteStep (invitar usuarios)
|
||||
└─ Step 4: CompleteStep → /dashboard
|
||||
```
|
||||
|
||||
### Flujo de Superadmin
|
||||
```
|
||||
Usuario con role=superadmin
|
||||
│
|
||||
├─► /dashboard/* → acceso normal
|
||||
│
|
||||
└─► /superadmin/* → acceso permitido
|
||||
│
|
||||
└─ /superadmin/tenants/:id → detalle tenant
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parámetros Dinámicos
|
||||
|
||||
| Ruta | Parámetro | Tipo | Descripción |
|
||||
|------|-----------|------|-------------|
|
||||
| `/dashboard/sales/leads/:id` | `id` | `string` (UUID) | ID del lead |
|
||||
| `/dashboard/sales/opportunities/:id` | `id` | `string` (UUID) | ID de la oportunidad |
|
||||
| `/superadmin/tenants/:id` | `id` | `string` (UUID) | ID del tenant |
|
||||
|
||||
### Uso en Componentes
|
||||
```tsx
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
function LeadDetailPage() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
// id contiene el UUID del lead
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Estado de Autenticación
|
||||
|
||||
### Store: `auth.store.ts`
|
||||
```typescript
|
||||
interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
role: string; // 'user' | 'admin' | 'superadmin'
|
||||
tenant_id: string;
|
||||
}
|
||||
|
||||
interface AuthState {
|
||||
user: User | null;
|
||||
accessToken: string | null;
|
||||
refreshToken: string | null;
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Resumen de Rutas
|
||||
|
||||
| Portal | Cantidad | Guard |
|
||||
|--------|----------|-------|
|
||||
| Auth | 3 rutas | GuestRoute |
|
||||
| Dashboard Core | 10 rutas | ProtectedRoute |
|
||||
| Dashboard Sales | 6 rutas | ProtectedRoute |
|
||||
| Dashboard Commissions | 5 rutas | ProtectedRoute |
|
||||
| Superadmin | 3 rutas | SuperadminRoute |
|
||||
| Onboarding | 1 ruta | ProtectedRoute |
|
||||
| **Total** | **28 rutas** | - |
|
||||
|
||||
---
|
||||
|
||||
*Documentación generada - Template SaaS v1.0.0*
|
||||
@ -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."
|
||||
|
||||
Loading…
Reference in New Issue
Block a user