shared-features 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/{admin-commonFeatures-BqSmAHvZ.js → admin-commonFeatures-CMRCJCHS.js} +3 -3
  2. package/dist/{admin-commonFeatures-BqSmAHvZ.js.map → admin-commonFeatures-CMRCJCHS.js.map} +1 -1
  3. package/dist/{admin-commonFeatures-LWADQRoD.cjs → admin-commonFeatures-CoJPCUYw.cjs} +27 -27
  4. package/dist/{admin-commonFeatures-LWADQRoD.cjs.map → admin-commonFeatures-CoJPCUYw.cjs.map} +1 -1
  5. package/dist/{broadcasts-D3_TQybH.cjs → broadcasts-CHTb-Z7-.cjs} +2 -2
  6. package/dist/{broadcasts-D3_TQybH.cjs.map → broadcasts-CHTb-Z7-.cjs.map} +1 -1
  7. package/dist/{broadcasts-B36bWyZf.js → broadcasts-Dkmto2dR.js} +2 -2
  8. package/dist/{broadcasts-B36bWyZf.js.map → broadcasts-Dkmto2dR.js.map} +1 -1
  9. package/dist/{commonFeatures-BNmLKLa9.js → commonFeatures-D-MZcecu.js} +5 -2
  10. package/dist/commonFeatures-D-MZcecu.js.map +1 -0
  11. package/dist/{commonFeatures-B9NKYWuL.cjs → commonFeatures-DTaIBhdj.cjs} +24 -21
  12. package/dist/commonFeatures-DTaIBhdj.cjs.map +1 -0
  13. package/dist/components/index.cjs +1 -1
  14. package/dist/components/index.js +1 -1
  15. package/dist/{commonFeatures-DhWaBEv_.cjs → featureFlags-C9iXfoJT.cjs} +116 -107
  16. package/dist/featureFlags-C9iXfoJT.cjs.map +1 -0
  17. package/dist/{commonFeatures-XJ9fuxg_.js → featureFlags-CqKSOF7q.js} +117 -107
  18. package/dist/featureFlags-CqKSOF7q.js.map +1 -0
  19. package/dist/firebase/config.d.ts +16 -7
  20. package/dist/firebase/config.d.ts.map +1 -1
  21. package/dist/firebase/init.d.ts.map +1 -1
  22. package/dist/hooks/index.cjs +2 -2
  23. package/dist/hooks/index.js +2 -2
  24. package/dist/{index-Dxc4v411.js → index-u8rmRNdW.js} +3 -3
  25. package/dist/{index-Dxc4v411.js.map → index-u8rmRNdW.js.map} +1 -1
  26. package/dist/{index-EGsyrMjD.cjs → index-z5yRtI-J.cjs} +3 -3
  27. package/dist/{index-EGsyrMjD.cjs.map → index-z5yRtI-J.cjs.map} +1 -1
  28. package/dist/index.cjs +80 -76
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.js +10 -9
  31. package/dist/services/index.cjs +52 -52
  32. package/dist/services/index.js +4 -4
  33. package/dist/types/commonFeatures.d.ts +18 -11
  34. package/dist/types/commonFeatures.d.ts.map +1 -1
  35. package/dist/types/index.cjs +21 -17
  36. package/dist/types/index.cjs.map +1 -1
  37. package/dist/types/index.js +3 -2
  38. package/dist/{useCommonFeatures-CwdJmhZE.cjs → useCommonFeatures-B0H0o03A.cjs} +2 -2
  39. package/dist/{useCommonFeatures-CwdJmhZE.cjs.map → useCommonFeatures-B0H0o03A.cjs.map} +1 -1
  40. package/dist/{useCommonFeatures-CZ6IM5Kn.js → useCommonFeatures-B2x-8atT.js} +2 -2
  41. package/dist/{useCommonFeatures-CZ6IM5Kn.js.map → useCommonFeatures-B2x-8atT.js.map} +1 -1
  42. package/dist/{useFeatureFlags-BHr1EOg0.js → useFeatureFlags-CeojCWSx.js} +3 -3
  43. package/dist/{useFeatureFlags-BHr1EOg0.js.map → useFeatureFlags-CeojCWSx.js.map} +1 -1
  44. package/dist/{useFeatureFlags-QBLhm28R.cjs → useFeatureFlags-Da05kQKA.cjs} +3 -3
  45. package/dist/{useFeatureFlags-QBLhm28R.cjs.map → useFeatureFlags-Da05kQKA.cjs.map} +1 -1
  46. package/package.json +1 -1
  47. package/dist/commonFeatures-B9NKYWuL.cjs.map +0 -1
  48. package/dist/commonFeatures-BNmLKLa9.js.map +0 -1
  49. package/dist/commonFeatures-DhWaBEv_.cjs.map +0 -1
  50. package/dist/commonFeatures-XJ9fuxg_.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/types/campaigns.ts","../../src/types/notifications.ts"],"sourcesContent":["/**\n * Campaign/Advertising System Type Definitions\n *\n * Type definitions for the centralized advertising system including\n * Firestore document schemas, service interfaces, and UI types.\n *\n * Based on ZTools advertising system, adapted for cross-project use.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { Timestamp } from 'firebase/firestore';\n\n// ============================================================================\n// PRODUCT TYPES\n// ============================================================================\n\n/**\n * Product type classification\n */\nexport type ProductType = 'extension' | 'android' | 'ios' | 'web';\n\n/**\n * Product information for advertising\n */\nexport interface Product {\n /** Unique product identifier */\n id: string;\n /** Product display name */\n name: string;\n /** Short tagline (marketing slogan) */\n tagline: string;\n /** Longer description of the product */\n description: string;\n /** Product type classification */\n type: ProductType;\n /** URL to the product (store link or website) */\n url: string;\n /** Brand/theme color (hex) */\n color: string;\n /** Key features list (3-4 items) */\n features: string[];\n /** Inline SVG icon (64px) */\n icon64?: string;\n /** Inline SVG icon (128px) */\n icon128?: string;\n /** Chrome Web Store URL */\n chromeStoreUrl?: string;\n /** Google Play Store URL */\n playStoreUrl?: string;\n /** Apple App Store URL */\n appStoreUrl?: string;\n /** Web app URL */\n webUrl?: string;\n /** Whether the product is active */\n enabled: boolean;\n /** When the product was created */\n createdAt: Timestamp;\n /** When the product was last updated */\n updatedAt: Timestamp;\n}\n\n// ============================================================================\n// CAMPAIGN TYPES\n// ============================================================================\n\n/**\n * Campaign status values\n */\nexport type CampaignStatus = 'active' | 'paused' | 'scheduled' | 'ended';\n\n/**\n * Target platforms for campaigns\n */\nexport type TargetPlatform = 'web' | 'android' | 'ios' | 'extension';\n\n/**\n * Target audience for campaigns\n */\nexport type TargetAudience = 'all' | 'authenticated' | 'anonymous';\n\n/**\n * Placement options for ads\n */\nexport type AdPlacement =\n | 'popup_slider'\n | 'options_panel'\n | 'onetime_modal'\n | 'update_modal'\n | 'notification'\n | 'footer_slider'\n | 'sidebar_panel'\n | 'home_banner';\n\n/**\n * Small panel variant styles\n */\nexport type SmallPanelVariant =\n | 'small_panel_1' // Minimal\n | 'small_panel_2' // Tagline\n | 'small_panel_3' // Features\n | 'small_panel_4' // Gradient\n | 'small_panel_5'; // Card\n\n/**\n * Large slider variant styles\n */\nexport type LargePanelVariant =\n | 'large_slider_1' // Hero\n | 'large_slider_2' // Feature Grid\n | 'large_slider_3' // Testimonial\n | 'large_slider_4' // Comparison\n | 'large_slider_5'; // Video Placeholder\n\n/**\n * All ad variant types\n */\nexport type AdVariant = SmallPanelVariant | LargePanelVariant;\n\n/**\n * Advertising campaign document\n * Stored in: zaions_campaigns/{campaignId}\n */\nexport interface Campaign {\n /** Campaign document ID */\n id: string;\n /** Reference to product being promoted */\n productId: string;\n /** Campaign name (admin reference) */\n name: string;\n /** Current campaign status */\n status: CampaignStatus;\n\n // === TARGETING ===\n /** Platforms where this campaign should show */\n targetPlatforms: TargetPlatform[];\n /** Audience type to target */\n targetAudience: TargetAudience;\n /** Specific projects to target (empty = all projects) */\n targetProjects: string[];\n /** Don't show to users already using this product */\n excludeProductUsers: boolean;\n\n // === DISPLAY RULES ===\n /** Where the ad can be displayed */\n placements: AdPlacement[];\n /** Priority (1-100, higher = more important) */\n priority: number;\n /** Days between impressions for same user (default: 20) */\n frequencyDays: number;\n /** Maximum total impressions (null = unlimited) */\n maxImpressions: number | null;\n\n // === TIMELINE ===\n /** Campaign start date */\n startDate: Timestamp;\n /** Campaign end date (null = no end) */\n endDate: Timestamp | null;\n\n // === CREATIVE ===\n /** UI variant to use */\n variant: AdVariant;\n /** Custom title (overrides product name) */\n customTitle?: string;\n /** Custom tagline (overrides product tagline) */\n customTagline?: string;\n /** Custom CTA button text */\n customCta?: string;\n /** Custom CTA button URL (overrides product URL) */\n customCtaUrl?: string;\n /** Custom description (overrides product description) */\n customDescription?: string;\n /** Custom product color for custom products/projects */\n customProductColor?: string;\n /** Custom icon SVG string (for custom products) */\n customIcon?: string;\n /** Custom features list (for custom products) */\n customFeatures?: string[];\n\n // === METRICS (Denormalized) ===\n /** Total number of impressions */\n totalImpressions: number;\n /** Total number of clicks */\n totalClicks: number;\n /** Total number of closes/dismissals */\n totalCloses: number;\n\n // === METADATA ===\n /** When campaign was created */\n createdAt: Timestamp;\n /** When campaign was last updated */\n updatedAt: Timestamp;\n /** Admin user ID who created */\n createdBy: string;\n /** Admin user ID who last updated */\n updatedBy?: string;\n}\n\n/**\n * Input for creating a new campaign\n */\nexport interface CreateCampaignInput {\n productId: string;\n name: string;\n status: CampaignStatus;\n targetPlatforms: TargetPlatform[];\n targetAudience: TargetAudience;\n targetProjects: string[];\n excludeProductUsers: boolean;\n placements: AdPlacement[];\n priority: number;\n frequencyDays: number;\n maxImpressions: number | null;\n startDate: Date;\n endDate: Date | null;\n variant: AdVariant;\n customTitle?: string;\n customTagline?: string;\n customCta?: string;\n customCtaUrl?: string;\n customDescription?: string;\n customProductColor?: string;\n customIcon?: string;\n customFeatures?: string[];\n}\n\n/**\n * Input for updating a campaign\n */\nexport interface UpdateCampaignInput extends Partial<CreateCampaignInput> {\n id: string;\n}\n\n// ============================================================================\n// IMPRESSION TYPES\n// ============================================================================\n\n/**\n * Types of ad interactions\n */\nexport type AdAction = 'impression' | 'click' | 'close' | 'notification_click';\n\n/**\n * Ad impression/interaction event\n * Stored in: zaions_impressions/{impressionId}\n */\nexport interface Impression {\n /** Impression document ID */\n id: string;\n /** Campaign that generated this impression */\n campaignId: string;\n /** Product being advertised */\n productId: string;\n /** Project where impression occurred */\n projectId: string;\n\n /** User ID (null for anonymous) */\n userId: string | null;\n /** Device/browser fingerprint */\n deviceId: string;\n /** Platform where impression occurred */\n platform: TargetPlatform;\n /** Placement where ad was shown */\n placement: AdPlacement;\n\n /** Type of interaction */\n action: AdAction;\n /** When the interaction occurred */\n timestamp: Timestamp;\n\n /** UI variant that was displayed */\n variant: AdVariant;\n /** Session identifier */\n sessionId?: string;\n}\n\n/**\n * Input for recording an impression\n */\nexport interface RecordImpressionInput {\n campaignId: string;\n productId: string;\n placement: AdPlacement;\n action: AdAction;\n variant: AdVariant;\n sessionId?: string;\n}\n\n// ============================================================================\n// AD HISTORY TYPES\n// ============================================================================\n\n/**\n * Local ad history entry (for frequency capping)\n */\nexport interface AdHistoryEntry {\n /** Campaign ID */\n campaignId: string;\n /** Product ID */\n productId: string;\n /** Last seen timestamp (Unix ms) */\n lastSeenAt: number;\n /** Impression count */\n impressionCount: number;\n /** Whether clicked */\n clicked: boolean;\n /** Whether closed */\n closed: boolean;\n /** Next eligible timestamp (Unix ms) */\n nextEligibleAt: number;\n}\n\n// ============================================================================\n// SERVICE TYPES\n// ============================================================================\n\n/**\n * Options for fetching campaigns\n */\nexport interface FetchCampaignsOptions {\n /** Filter by placement */\n placement?: AdPlacement;\n /** Filter by status */\n status?: CampaignStatus;\n /** Filter by product */\n productId?: string;\n /** Maximum results */\n limit?: number;\n}\n\n/**\n * Campaign with resolved product data\n */\nexport interface CampaignWithProduct extends Campaign {\n /** Resolved product information */\n product: Product;\n}\n\n// ============================================================================\n// ANALYTICS TYPES\n// ============================================================================\n\n/**\n * Campaign analytics summary\n */\nexport interface CampaignAnalytics {\n campaignId: string;\n campaignName: string;\n productId: string;\n productName: string;\n\n /** Total impressions */\n impressions: number;\n /** Total clicks */\n clicks: number;\n /** Total closes */\n closes: number;\n /** Click-through rate (clicks/impressions) */\n ctr: number;\n /** Close rate (closes/impressions) */\n closeRate: number;\n\n /** Breakdown by platform */\n byPlatform: Record<TargetPlatform, { impressions: number; clicks: number }>;\n /** Breakdown by placement */\n byPlacement: Record<AdPlacement, { impressions: number; clicks: number }>;\n /** Breakdown by project */\n byProject: Record<string, { impressions: number; clicks: number }>;\n /** Daily breakdown */\n byDate: Array<{\n date: string;\n impressions: number;\n clicks: number;\n closes: number;\n }>;\n}\n\n// ============================================================================\n// COMPONENT PROPS\n// ============================================================================\n\n/**\n * Props for small panel variant components\n */\nexport interface SmallPanelProps {\n /** Campaign with product data */\n campaign: CampaignWithProduct;\n /** Called when user clicks CTA */\n onCTAClick?: () => void;\n /** Called when user closes/dismisses the ad */\n onClose?: () => void;\n}\n\n/**\n * Props for large panel variant components\n */\nexport interface LargePanelProps {\n /** Campaign with product data */\n campaign: CampaignWithProduct;\n /** Called when user clicks CTA */\n onCTAClick?: () => void;\n /** Called when user closes/dismisses the ad */\n onClose?: () => void;\n /** Whether to show slide indicator */\n showIndicator?: boolean;\n /** Current slide index (for carousel) */\n currentIndex?: number;\n /** Total number of slides (for carousel) */\n totalCount?: number;\n}\n\n/**\n * Props for ad panel component\n */\nexport interface AdPanelProps {\n placement: AdPlacement;\n variant?: SmallPanelVariant;\n className?: string;\n}\n\n// ============================================================================\n// DEFAULT VALUES\n// ============================================================================\n\n/**\n * Default frequency cap in days\n */\nexport const DEFAULT_FREQUENCY_DAYS = 20;\n\n/**\n * Default campaign priority\n */\nexport const DEFAULT_CAMPAIGN_PRIORITY = 50;\n\n/**\n * Variant display names\n */\nexport const VARIANT_NAMES: Record<AdVariant, string> = {\n small_panel_1: 'Minimal',\n small_panel_2: 'Tagline',\n small_panel_3: 'Features',\n small_panel_4: 'Gradient',\n small_panel_5: 'Card',\n large_slider_1: 'Hero',\n large_slider_2: 'Feature Grid',\n large_slider_3: 'Testimonial',\n large_slider_4: 'Comparison',\n large_slider_5: 'Video Placeholder',\n};\n\n/**\n * Placement display names\n */\nexport const PLACEMENT_NAMES: Record<AdPlacement, string> = {\n popup_slider: 'Extension Popup Slider',\n options_panel: 'Extension Options Panel',\n onetime_modal: 'One-Time Welcome Modal',\n update_modal: 'Update Announcement Modal',\n notification: 'Browser Notification',\n footer_slider: 'Web App Footer Slider',\n sidebar_panel: 'Web App Sidebar Panel',\n home_banner: 'Home Page Banner',\n};\n\n/**\n * Platform display names\n */\nexport const PLATFORM_NAMES: Record<TargetPlatform, string> = {\n web: 'Web App',\n android: 'Android App',\n ios: 'iOS App',\n extension: 'Browser Extension',\n};\n\n// ============================================================================\n// FIREBASE COLLECTION NAMES\n// ============================================================================\n\n/**\n * Collection names for shared features\n */\nexport const COLLECTIONS = {\n CAMPAIGNS: 'zaions_campaigns',\n PRODUCTS: 'zaions_products',\n IMPRESSIONS: 'zaions_impressions',\n CONTACTS: 'zaions_contacts',\n FEATURE_REQUESTS: 'zaions_feature_requests',\n PAYMENT_OPTIONS: 'zaions_payment_options',\n SOCIAL_LINKS: 'zaions_social_links',\n DEVELOPER_INFO: 'zaions_developer_info',\n} as const;\n","/**\n * In-App Notification System Type Definitions\n *\n * Type definitions for the unified notification system including\n * broadcasts, user notifications, templates, and preferences.\n *\n * Architecture:\n * - Broadcasts: Stored in shared-features Firebase (zaions_broadcasts)\n * - User notifications: Stored in consumer project's Firebase\n * - Templates: Stored in shared-features Firebase (zaions_notification_templates)\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { Timestamp } from 'firebase/firestore';\n\n// ============================================================================\n// NOTIFICATION CATEGORIES & TYPES\n// ============================================================================\n\n/**\n * Standard notification categories for grouping and filtering\n */\nexport type NotificationCategory =\n | 'system' // Maintenance, updates, outages\n | 'account' // Welcome, security, profile changes\n | 'activity' // CRUD operations, user actions\n | 'report' // Weekly/monthly summaries\n | 'promotional' // Tips, features, holidays\n | 'social'; // Shares, mentions, comments\n\n/**\n * Visual notification type for styling\n */\nexport type NotificationType =\n | 'info' // General information\n | 'success' // Positive feedback\n | 'warning' // Attention needed\n | 'error' // Something went wrong\n | 'reminder' // Time-based reminder\n | 'milestone' // Achievement/progress\n | 'announcement'; // Important announcement\n\n/**\n * Priority levels for notifications\n */\nexport type NotificationPriority = 'urgent' | 'high' | 'normal' | 'low';\n\n/**\n * Source of the notification\n */\nexport type NotificationSource =\n | 'system' // Auto-generated by system\n | 'event' // Triggered by user action\n | 'broadcast' // Cross-project announcement\n | 'admin'; // Sent by admin\n\n/**\n * Broadcast display variants\n */\nexport type BroadcastVariant = 'banner' | 'modal' | 'toast' | 'bell';\n\n/**\n * Broadcast status\n */\nexport type BroadcastStatus = 'draft' | 'scheduled' | 'active' | 'ended';\n\n/**\n * Target platforms for notifications\n */\nexport type NotificationPlatform = 'web' | 'android' | 'ios';\n\n/**\n * Target audience for broadcasts\n */\nexport type NotificationAudience = 'all' | 'authenticated' | 'anonymous';\n\n// ============================================================================\n// STANDARD EVENT TYPES\n// ============================================================================\n\n/**\n * Standard event types that trigger notifications\n */\nexport type StandardEventType =\n // Account Events\n | 'ACCOUNT_CREATED'\n | 'ACCOUNT_WELCOME'\n | 'NEW_DEVICE_SIGNIN'\n | 'PASSWORD_CHANGED'\n | 'PROFILE_UPDATED'\n | 'ACCOUNT_DELETED'\n // Report Events\n | 'DAILY_SUMMARY'\n | 'WEEKLY_SUMMARY'\n | 'MONTHLY_SUMMARY'\n | 'QUARTERLY_SUMMARY'\n | 'YEARLY_SUMMARY'\n // Promotional Events\n | 'APP_TIP'\n | 'HIDDEN_FEATURE'\n | 'NEW_FEATURE_ANNOUNCEMENT'\n | 'HOLIDAY_GREETING'\n // System Events\n | 'SYSTEM_MAINTENANCE'\n | 'APP_UPDATE_AVAILABLE'\n | 'DATA_EXPORT_READY'\n // Activity Events (Generic CRUD)\n | 'ITEM_CREATED'\n | 'ITEM_UPDATED'\n | 'ITEM_DELETED'\n | 'BULK_OPERATION_COMPLETE';\n\n// ============================================================================\n// BASE NOTIFICATION INTERFACE\n// ============================================================================\n\n/**\n * Base notification fields shared across all notification types\n */\nexport interface BaseNotification {\n /** Unique notification ID */\n id: string;\n /** Notification title */\n title: string;\n /** Notification message/body */\n message: string;\n /** Visual type for styling */\n type: NotificationType;\n /** Category for grouping/filtering */\n category: NotificationCategory;\n /** Whether the notification has been read */\n isRead: boolean;\n /** Whether this notification is important/pinned */\n isImportant?: boolean;\n /** Optional action URL to navigate to */\n actionUrl?: string;\n /** Optional action button text */\n actionText?: string;\n /** When the notification was created */\n createdAt: Date | Timestamp;\n /** When the notification was read */\n readAt?: Date | Timestamp;\n /** Additional metadata for custom use */\n metadata?: Record<string, unknown>;\n}\n\n// ============================================================================\n// USER NOTIFICATION\n// ============================================================================\n\n/**\n * User-specific notification stored in consumer project's Firebase\n * Collection: {prefix}_notifications/{notificationId}\n */\nexport interface UserNotification extends BaseNotification {\n /** User ID this notification belongs to */\n userId: string;\n /** How the notification was generated */\n source: NotificationSource;\n /** Reference to source entity (broadcast ID, event ID, etc.) */\n sourceId?: string;\n /** Event type if triggered by event */\n eventType?: StandardEventType | string;\n /** When the notification expires (auto-delete) */\n expiresAt?: Date | Timestamp;\n}\n\n/**\n * Input for creating a user notification\n */\nexport interface CreateUserNotificationInput {\n userId: string;\n title: string;\n message: string;\n type: NotificationType;\n category: NotificationCategory;\n source: NotificationSource;\n sourceId?: string;\n eventType?: StandardEventType | string;\n isImportant?: boolean;\n actionUrl?: string;\n actionText?: string;\n expiresAt?: Date;\n metadata?: Record<string, unknown>;\n}\n\n// ============================================================================\n// BROADCAST NOTIFICATION\n// ============================================================================\n\n/**\n * Cross-project broadcast notification stored in shared-features Firebase\n * Collection: zaions_broadcasts/{broadcastId}\n */\nexport interface BroadcastNotification extends BaseNotification {\n /** Target project IDs (empty = all projects) */\n targetProjects: string[];\n /** Target platforms */\n targetPlatforms: NotificationPlatform[];\n /** Target audience */\n targetAudience: NotificationAudience;\n /** Broadcast status */\n status: BroadcastStatus;\n /** When the broadcast becomes active */\n startDate: Date | Timestamp;\n /** When the broadcast ends (null = no end) */\n endDate?: Date | Timestamp | null;\n /** Display priority (1-100, higher = more important) */\n priority: number;\n /** Whether user can dismiss this broadcast */\n dismissible: boolean;\n /** How to display this broadcast */\n variant: BroadcastVariant;\n /** Total impressions count */\n impressions: number;\n /** Total clicks count */\n clicks: number;\n /** Admin user ID who created */\n createdBy: string;\n /** Admin user ID who last updated */\n updatedBy?: string;\n}\n\n/**\n * Input for creating a broadcast\n */\nexport interface CreateBroadcastInput {\n title: string;\n message: string;\n type: NotificationType;\n category: NotificationCategory;\n targetProjects: string[];\n targetPlatforms: NotificationPlatform[];\n targetAudience: NotificationAudience;\n startDate: Date;\n endDate?: Date | null;\n priority: number;\n dismissible: boolean;\n variant: BroadcastVariant;\n isImportant?: boolean;\n actionUrl?: string;\n actionText?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Input for updating a broadcast\n */\nexport interface UpdateBroadcastInput extends Partial<CreateBroadcastInput> {\n id: string;\n status?: BroadcastStatus;\n}\n\n// ============================================================================\n// NOTIFICATION TEMPLATE\n// ============================================================================\n\n/**\n * Notification template for standard events\n * Collection: zaions_notification_templates/{templateId}\n */\nexport interface NotificationTemplate {\n /** Template ID */\n id: string;\n /** Template name for admin reference */\n name: string;\n /** Event type this template is for */\n eventType: StandardEventType | string;\n /** Notification category */\n category: NotificationCategory;\n /** Title with placeholders: \"Welcome to {{appName}}, {{userName}}!\" */\n title: string;\n /** Message with placeholders */\n message: string;\n /** List of variable names used in title/message */\n variables: string[];\n /** Visual notification type */\n type: NotificationType;\n /** Whether notifications from this template are important */\n isImportant: boolean;\n /** Default action URL (can use placeholders) */\n actionUrl?: string;\n /** Default action button text */\n actionText?: string;\n /** Whether this template is enabled */\n enabled: boolean;\n /** When template was created */\n createdAt: Date | Timestamp;\n /** When template was last updated */\n updatedAt: Date | Timestamp;\n}\n\n/**\n * Input for creating a template\n */\nexport interface CreateTemplateInput {\n name: string;\n eventType: StandardEventType | string;\n category: NotificationCategory;\n title: string;\n message: string;\n variables: string[];\n type: NotificationType;\n isImportant: boolean;\n actionUrl?: string;\n actionText?: string;\n enabled: boolean;\n}\n\n// ============================================================================\n// USER NOTIFICATION PREFERENCES\n// ============================================================================\n\n/**\n * Category preference settings\n */\nexport interface CategoryPreference {\n /** Show in-app notifications for this category */\n inApp: boolean;\n /** Send push notifications for this category */\n push: boolean;\n /** Send email notifications for this category */\n email: boolean;\n}\n\n/**\n * Quiet hours configuration\n */\nexport interface QuietHoursConfig {\n /** Whether quiet hours are enabled */\n enabled: boolean;\n /** Start time in 24h format: \"22:00\" */\n start: string;\n /** End time in 24h format: \"08:00\" */\n end: string;\n /** User's timezone (IANA format) */\n timezone: string;\n}\n\n/**\n * Email digest configuration\n */\nexport interface EmailDigestConfig {\n /** Whether email digest is enabled */\n enabled: boolean;\n /** Digest frequency */\n frequency: 'daily' | 'weekly' | 'none';\n}\n\n/**\n * Push notification configuration\n */\nexport interface PushConfig {\n /** Whether push is enabled */\n enabled: boolean;\n /** OneSignal player ID */\n playerId?: string;\n /** Web push subscription */\n webPushSubscription?: PushSubscription;\n}\n\n/**\n * User notification preferences\n * Collection: {prefix}_notification_preferences/{userId}\n */\nexport interface NotificationPreferences {\n /** User ID */\n userId: string;\n /** Category-specific preferences */\n categories: Record<NotificationCategory, CategoryPreference>;\n /** Quiet hours settings */\n quietHours: QuietHoursConfig;\n /** Email digest settings */\n emailDigest: EmailDigestConfig;\n /** Push notification settings */\n push: PushConfig;\n /** When preferences were last updated */\n updatedAt: Date | Timestamp;\n}\n\n/**\n * Default notification preferences\n */\nexport const DEFAULT_NOTIFICATION_PREFERENCES: Omit<\n NotificationPreferences,\n 'userId' | 'updatedAt'\n> = {\n categories: {\n system: { inApp: true, push: true, email: false },\n account: { inApp: true, push: true, email: true },\n activity: { inApp: true, push: false, email: false },\n report: { inApp: true, push: false, email: true },\n promotional: { inApp: true, push: false, email: false },\n social: { inApp: true, push: true, email: false },\n },\n quietHours: {\n enabled: false,\n start: '22:00',\n end: '08:00',\n timezone: 'UTC',\n },\n emailDigest: {\n enabled: false,\n frequency: 'none',\n },\n push: {\n enabled: false,\n },\n};\n\n// ============================================================================\n// NOTIFICATION EVENTS\n// ============================================================================\n\n/**\n * Event definition for triggering notifications\n */\nexport interface NotificationEventDefinition {\n /** Event type identifier */\n type: StandardEventType | string;\n /** Event display name */\n name: string;\n /** Event description */\n description: string;\n /** Default notification category */\n category: NotificationCategory;\n /** Default notification type */\n notificationType: NotificationType;\n /** Default priority */\n priority: NotificationPriority;\n /** Available template variables */\n variables: string[];\n /** Whether this event is enabled by default */\n defaultEnabled: boolean;\n}\n\n/**\n * Event trigger payload\n */\nexport interface NotificationEventPayload {\n /** Event type */\n type: StandardEventType | string;\n /** User ID to notify */\n userId: string;\n /** Variable values for template interpolation */\n data: Record<string, string | number | boolean>;\n /** Optional override for notification title */\n titleOverride?: string;\n /** Optional override for notification message */\n messageOverride?: string;\n /** Optional action URL */\n actionUrl?: string;\n /** Optional action text */\n actionText?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n// ============================================================================\n// ANALYTICS TYPES\n// ============================================================================\n\n/**\n * Broadcast analytics summary\n */\nexport interface BroadcastAnalytics {\n broadcastId: string;\n title: string;\n status: BroadcastStatus;\n impressions: number;\n clicks: number;\n dismissals: number;\n ctr: number;\n byPlatform: Record<NotificationPlatform, { impressions: number; clicks: number }>;\n byProject: Record<string, { impressions: number; clicks: number }>;\n byDate: Array<{\n date: string;\n impressions: number;\n clicks: number;\n }>;\n}\n\n/**\n * Overall notification analytics\n */\nexport interface NotificationAnalytics {\n /** Total notifications sent */\n totalSent: number;\n /** Total notifications read */\n totalRead: number;\n /** Total notifications clicked */\n totalClicked: number;\n /** Read rate (read/sent) */\n readRate: number;\n /** Click rate (clicked/sent) */\n clickRate: number;\n /** Breakdown by category */\n byCategory: Record<\n NotificationCategory,\n {\n sent: number;\n read: number;\n clicked: number;\n }\n >;\n /** Breakdown by type */\n byType: Record<\n NotificationType,\n {\n sent: number;\n read: number;\n clicked: number;\n }\n >;\n /** Daily breakdown */\n byDate: Array<{\n date: string;\n sent: number;\n read: number;\n clicked: number;\n }>;\n}\n\n// ============================================================================\n// COMPONENT PROPS\n// ============================================================================\n\n/**\n * Props for NotificationBell component\n */\nexport interface NotificationBellProps {\n /** Additional CSS classes */\n className?: string;\n /** Icon size in pixels */\n size?: number;\n /** Show count badge even when 0 */\n showZeroBadge?: boolean;\n /** Custom onClick handler (default: toggle panel) */\n onClick?: () => void;\n}\n\n/**\n * Props for NotificationPanel component\n */\nexport interface NotificationPanelProps {\n /** Whether the panel is open */\n isOpen: boolean;\n /** Called when panel should close */\n onClose: () => void;\n /** Maximum height for the panel */\n maxHeight?: string;\n /** Whether to show the filter tabs */\n showFilters?: boolean;\n /** Custom empty state component */\n emptyStateComponent?: React.ReactNode;\n}\n\n/**\n * Props for NotificationCard component\n */\nexport interface NotificationCardProps {\n /** The notification to display */\n notification: UserNotification;\n /** Called when notification is clicked */\n onClick?: () => void;\n /** Called when delete button is clicked */\n onDelete?: () => void;\n /** Whether to show delete button */\n showDelete?: boolean;\n /** Whether to show timestamp */\n showTimestamp?: boolean;\n}\n\n/**\n * Props for BroadcastBanner component\n */\nexport interface BroadcastBannerProps {\n /** The broadcast to display */\n broadcast: BroadcastNotification;\n /** Called when CTA is clicked */\n onActionClick?: () => void;\n /** Called when banner is dismissed */\n onDismiss?: () => void;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Props for AnnouncementModal component\n */\nexport interface AnnouncementModalProps {\n /** The broadcast to display */\n broadcast: BroadcastNotification;\n /** Whether the modal is open */\n isOpen: boolean;\n /** Called when modal should close */\n onClose: () => void;\n /** Called when CTA is clicked */\n onActionClick?: () => void;\n}\n\n/**\n * Props for NotificationPreferences component\n */\nexport interface NotificationPreferencesProps {\n /** Current preferences */\n preferences: NotificationPreferences;\n /** Called when preferences change */\n onPreferencesChange: (preferences: NotificationPreferences) => void;\n /** Whether saving is in progress */\n isSaving?: boolean;\n /** Categories to show (default: all) */\n visibleCategories?: NotificationCategory[];\n /** Whether to show push settings */\n showPushSettings?: boolean;\n /** Whether to show email settings */\n showEmailSettings?: boolean;\n /** Whether to show quiet hours settings */\n showQuietHours?: boolean;\n}\n\n// ============================================================================\n// HOOK RETURN TYPES\n// ============================================================================\n\n/**\n * Return type for useNotifications hook\n */\nexport interface UseNotificationsReturn {\n /** List of notifications */\n notifications: UserNotification[];\n /** Number of unread notifications */\n unreadCount: number;\n /** Whether notifications are loading */\n isLoading: boolean;\n /** Error if any */\n error: Error | null;\n /** Mark a notification as read */\n markAsRead: (notificationId: string) => Promise<void>;\n /** Mark all notifications as read */\n markAllAsRead: () => Promise<void>;\n /** Delete a notification */\n deleteNotification: (notificationId: string) => Promise<void>;\n /** Clear all notifications */\n clearAll: () => Promise<void>;\n /** Refresh notifications */\n refresh: () => Promise<void>;\n}\n\n/**\n * Return type for useBroadcasts hook\n */\nexport interface UseBroadcastsReturn {\n /** List of active broadcasts */\n broadcasts: BroadcastNotification[];\n /** Whether broadcasts are loading */\n isLoading: boolean;\n /** Error if any */\n error: Error | null;\n /** Dismiss a broadcast */\n dismissBroadcast: (broadcastId: string) => void;\n /** Track broadcast impression */\n trackImpression: (broadcastId: string) => void;\n /** Track broadcast click */\n trackClick: (broadcastId: string) => void;\n /** Check if broadcast was dismissed */\n isDismissed: (broadcastId: string) => boolean;\n /** Refresh broadcasts from cache */\n refresh: () => Promise<void>;\n}\n\n/**\n * Return type for useNotificationEvents hook\n */\nexport interface UseNotificationEventsReturn {\n /** Trigger a notification event */\n trigger: (payload: NotificationEventPayload) => Promise<void>;\n /** Trigger account created event */\n triggerAccountCreated: (data: { userName: string; email: string }) => Promise<void>;\n /** Trigger new device signin event */\n triggerNewDeviceSignin: (data: { deviceName: string; location?: string }) => Promise<void>;\n /** Trigger password changed event */\n triggerPasswordChanged: () => Promise<void>;\n /** Trigger profile updated event */\n triggerProfileUpdated: (data: { changes: string[] }) => Promise<void>;\n /** Trigger CRUD event */\n triggerCrudEvent: (data: {\n action: 'created' | 'updated' | 'deleted';\n entityType: string;\n entityName: string;\n }) => Promise<void>;\n /** Trigger app tip event */\n triggerAppTip: (data: { tipId: string; title: string; body: string }) => Promise<void>;\n}\n\n// ============================================================================\n// SERVICE TYPES\n// ============================================================================\n\n/**\n * Options for fetching broadcasts\n */\nexport interface FetchBroadcastsOptions {\n /** Filter by project ID */\n projectId?: string;\n /** Filter by platform */\n platform?: NotificationPlatform;\n /** Filter by variant */\n variant?: BroadcastVariant;\n /** Filter by status */\n status?: BroadcastStatus;\n /** Maximum results */\n limit?: number;\n}\n\n/**\n * Options for fetching user notifications\n */\nexport interface FetchNotificationsOptions {\n /** User ID */\n userId: string;\n /** Filter by category */\n category?: NotificationCategory;\n /** Filter by read status */\n isRead?: boolean;\n /** Maximum results */\n limit?: number;\n /** Offset for pagination */\n offset?: number;\n /** Start date filter */\n startDate?: Date;\n /** End date filter */\n endDate?: Date;\n}\n\n// ============================================================================\n// COLLECTION NAMES\n// ============================================================================\n\n/**\n * Shared collections (stored in shared-features Firebase)\n */\nexport const NOTIFICATION_COLLECTIONS = {\n /** Cross-project broadcasts */\n BROADCASTS: 'zaions_broadcasts',\n /** Notification templates */\n TEMPLATES: 'zaions_notification_templates',\n /** Broadcast events/analytics */\n BROADCAST_EVENTS: 'zaions_broadcast_events',\n} as const;\n\n/**\n * Get consumer project collection names\n * @param prefix - Project prefix (e.g., 'pp' for pregnancy-pal)\n */\nexport const getNotificationCollections = (prefix: string) =>\n ({\n /** User notifications */\n NOTIFICATIONS: `${prefix}_notifications`,\n /** User notification preferences */\n PREFERENCES: `${prefix}_notification_preferences`,\n /** Push subscriptions */\n PUSH_SUBSCRIPTIONS: `${prefix}_push_subscriptions`,\n }) as const;\n\n// ============================================================================\n// DISPLAY NAMES & CONSTANTS\n// ============================================================================\n\n/**\n * Category display names\n */\nexport const CATEGORY_NAMES: Record<NotificationCategory, string> = {\n system: 'System',\n account: 'Account',\n activity: 'Activity',\n report: 'Reports',\n promotional: 'Tips & Updates',\n social: 'Social',\n};\n\n/**\n * Category descriptions\n */\nexport const CATEGORY_DESCRIPTIONS: Record<NotificationCategory, string> = {\n system: 'Maintenance updates, outages, and system alerts',\n account: 'Welcome messages, security alerts, and profile changes',\n activity: 'Updates about your actions and changes',\n report: 'Weekly and monthly summaries of your activity',\n promotional: 'Tips, new features, and holiday greetings',\n social: 'Mentions, shares, and comments from others',\n};\n\n/**\n * Notification type display names\n */\nexport const TYPE_NAMES: Record<NotificationType, string> = {\n info: 'Information',\n success: 'Success',\n warning: 'Warning',\n error: 'Error',\n reminder: 'Reminder',\n milestone: 'Milestone',\n announcement: 'Announcement',\n};\n\n/**\n * Priority display names\n */\nexport const PRIORITY_NAMES: Record<NotificationPriority, string> = {\n urgent: 'Urgent',\n high: 'High',\n normal: 'Normal',\n low: 'Low',\n};\n\n/**\n * Event type display names\n */\nexport const EVENT_TYPE_NAMES: Record<StandardEventType, string> = {\n ACCOUNT_CREATED: 'Account Created',\n ACCOUNT_WELCOME: 'Welcome Message',\n NEW_DEVICE_SIGNIN: 'New Device Sign-in',\n PASSWORD_CHANGED: 'Password Changed',\n PROFILE_UPDATED: 'Profile Updated',\n ACCOUNT_DELETED: 'Account Deleted',\n DAILY_SUMMARY: 'Daily Summary',\n WEEKLY_SUMMARY: 'Weekly Summary',\n MONTHLY_SUMMARY: 'Monthly Summary',\n QUARTERLY_SUMMARY: 'Quarterly Summary',\n YEARLY_SUMMARY: 'Yearly Summary',\n APP_TIP: 'App Tip',\n HIDDEN_FEATURE: 'Hidden Feature',\n NEW_FEATURE_ANNOUNCEMENT: 'New Feature',\n HOLIDAY_GREETING: 'Holiday Greeting',\n SYSTEM_MAINTENANCE: 'System Maintenance',\n APP_UPDATE_AVAILABLE: 'App Update Available',\n DATA_EXPORT_READY: 'Data Export Ready',\n ITEM_CREATED: 'Item Created',\n ITEM_UPDATED: 'Item Updated',\n ITEM_DELETED: 'Item Deleted',\n BULK_OPERATION_COMPLETE: 'Bulk Operation Complete',\n};\n\n/**\n * Default broadcast priority\n */\nexport const DEFAULT_BROADCAST_PRIORITY = 50;\n\n/**\n * Maximum notification title length\n */\nexport const MAX_NOTIFICATION_TITLE_LENGTH = 100;\n\n/**\n * Maximum notification message length\n */\nexport const MAX_NOTIFICATION_MESSAGE_LENGTH = 500;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/types/campaigns.ts","../../src/types/notifications.ts"],"sourcesContent":["/**\n * Campaign/Advertising System Type Definitions\n *\n * Type definitions for the centralized advertising system including\n * Firestore document schemas, service interfaces, and UI types.\n *\n * Based on ZTools advertising system, adapted for cross-project use.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { Timestamp } from 'firebase/firestore';\n\n// ============================================================================\n// PRODUCT TYPES\n// ============================================================================\n\n/**\n * Product type classification\n */\nexport type ProductType = 'extension' | 'android' | 'ios' | 'web';\n\n/**\n * Product information for advertising\n */\nexport interface Product {\n /** Unique product identifier */\n id: string;\n /** Product display name */\n name: string;\n /** Short tagline (marketing slogan) */\n tagline: string;\n /** Longer description of the product */\n description: string;\n /** Product type classification */\n type: ProductType;\n /** URL to the product (store link or website) */\n url: string;\n /** Brand/theme color (hex) */\n color: string;\n /** Key features list (3-4 items) */\n features: string[];\n /** Inline SVG icon (64px) */\n icon64?: string;\n /** Inline SVG icon (128px) */\n icon128?: string;\n /** Chrome Web Store URL */\n chromeStoreUrl?: string;\n /** Google Play Store URL */\n playStoreUrl?: string;\n /** Apple App Store URL */\n appStoreUrl?: string;\n /** Web app URL */\n webUrl?: string;\n /** Whether the product is active */\n enabled: boolean;\n /** When the product was created */\n createdAt: Timestamp;\n /** When the product was last updated */\n updatedAt: Timestamp;\n}\n\n// ============================================================================\n// CAMPAIGN TYPES\n// ============================================================================\n\n/**\n * Campaign status values\n */\nexport type CampaignStatus = 'active' | 'paused' | 'scheduled' | 'ended';\n\n/**\n * Target platforms for campaigns\n */\nexport type TargetPlatform = 'web' | 'android' | 'ios' | 'extension';\n\n/**\n * Target audience for campaigns\n */\nexport type TargetAudience = 'all' | 'authenticated' | 'anonymous';\n\n/**\n * Placement options for ads\n */\nexport type AdPlacement =\n | 'popup_slider'\n | 'options_panel'\n | 'onetime_modal'\n | 'update_modal'\n | 'notification'\n | 'footer_slider'\n | 'sidebar_panel'\n | 'home_banner';\n\n/**\n * Small panel variant styles\n */\nexport type SmallPanelVariant =\n | 'small_panel_1' // Minimal\n | 'small_panel_2' // Tagline\n | 'small_panel_3' // Features\n | 'small_panel_4' // Gradient\n | 'small_panel_5'; // Card\n\n/**\n * Large slider variant styles\n */\nexport type LargePanelVariant =\n | 'large_slider_1' // Hero\n | 'large_slider_2' // Feature Grid\n | 'large_slider_3' // Testimonial\n | 'large_slider_4' // Comparison\n | 'large_slider_5'; // Video Placeholder\n\n/**\n * All ad variant types\n */\nexport type AdVariant = SmallPanelVariant | LargePanelVariant;\n\n/**\n * Advertising campaign document\n * Stored in: zaions_campaigns/{campaignId}\n */\nexport interface Campaign {\n /** Campaign document ID */\n id: string;\n /** Reference to product being promoted */\n productId: string;\n /** Campaign name (admin reference) */\n name: string;\n /** Current campaign status */\n status: CampaignStatus;\n\n // === TARGETING ===\n /** Platforms where this campaign should show */\n targetPlatforms: TargetPlatform[];\n /** Audience type to target */\n targetAudience: TargetAudience;\n /** Specific projects to target (empty = all projects) */\n targetProjects: string[];\n /** Don't show to users already using this product */\n excludeProductUsers: boolean;\n\n // === DISPLAY RULES ===\n /** Where the ad can be displayed */\n placements: AdPlacement[];\n /** Priority (1-100, higher = more important) */\n priority: number;\n /** Days between impressions for same user (default: 20) */\n frequencyDays: number;\n /** Maximum total impressions (null = unlimited) */\n maxImpressions: number | null;\n\n // === TIMELINE ===\n /** Campaign start date */\n startDate: Timestamp;\n /** Campaign end date (null = no end) */\n endDate: Timestamp | null;\n\n // === CREATIVE ===\n /** UI variant to use */\n variant: AdVariant;\n /** Custom title (overrides product name) */\n customTitle?: string;\n /** Custom tagline (overrides product tagline) */\n customTagline?: string;\n /** Custom CTA button text */\n customCta?: string;\n /** Custom CTA button URL (overrides product URL) */\n customCtaUrl?: string;\n /** Custom description (overrides product description) */\n customDescription?: string;\n /** Custom product color for custom products/projects */\n customProductColor?: string;\n /** Custom icon SVG string (for custom products) */\n customIcon?: string;\n /** Custom features list (for custom products) */\n customFeatures?: string[];\n\n // === METRICS (Denormalized) ===\n /** Total number of impressions */\n totalImpressions: number;\n /** Total number of clicks */\n totalClicks: number;\n /** Total number of closes/dismissals */\n totalCloses: number;\n\n // === METADATA ===\n /** When campaign was created */\n createdAt: Timestamp;\n /** When campaign was last updated */\n updatedAt: Timestamp;\n /** Admin user ID who created */\n createdBy: string;\n /** Admin user ID who last updated */\n updatedBy?: string;\n}\n\n/**\n * Input for creating a new campaign\n */\nexport interface CreateCampaignInput {\n productId: string;\n name: string;\n status: CampaignStatus;\n targetPlatforms: TargetPlatform[];\n targetAudience: TargetAudience;\n targetProjects: string[];\n excludeProductUsers: boolean;\n placements: AdPlacement[];\n priority: number;\n frequencyDays: number;\n maxImpressions: number | null;\n startDate: Date;\n endDate: Date | null;\n variant: AdVariant;\n customTitle?: string;\n customTagline?: string;\n customCta?: string;\n customCtaUrl?: string;\n customDescription?: string;\n customProductColor?: string;\n customIcon?: string;\n customFeatures?: string[];\n}\n\n/**\n * Input for updating a campaign\n */\nexport interface UpdateCampaignInput extends Partial<CreateCampaignInput> {\n id: string;\n}\n\n// ============================================================================\n// IMPRESSION TYPES\n// ============================================================================\n\n/**\n * Types of ad interactions\n */\nexport type AdAction = 'impression' | 'click' | 'close' | 'notification_click';\n\n/**\n * Ad impression/interaction event\n * Stored in: zaions_impressions/{impressionId}\n */\nexport interface Impression {\n /** Impression document ID */\n id: string;\n /** Campaign that generated this impression */\n campaignId: string;\n /** Product being advertised */\n productId: string;\n /** Project where impression occurred */\n projectId: string;\n\n /** User ID (null for anonymous) */\n userId: string | null;\n /** Device/browser fingerprint */\n deviceId: string;\n /** Platform where impression occurred */\n platform: TargetPlatform;\n /** Placement where ad was shown */\n placement: AdPlacement;\n\n /** Type of interaction */\n action: AdAction;\n /** When the interaction occurred */\n timestamp: Timestamp;\n\n /** UI variant that was displayed */\n variant: AdVariant;\n /** Session identifier */\n sessionId?: string;\n}\n\n/**\n * Input for recording an impression\n */\nexport interface RecordImpressionInput {\n campaignId: string;\n productId: string;\n placement: AdPlacement;\n action: AdAction;\n variant: AdVariant;\n sessionId?: string;\n}\n\n// ============================================================================\n// AD HISTORY TYPES\n// ============================================================================\n\n/**\n * Local ad history entry (for frequency capping)\n */\nexport interface AdHistoryEntry {\n /** Campaign ID */\n campaignId: string;\n /** Product ID */\n productId: string;\n /** Last seen timestamp (Unix ms) */\n lastSeenAt: number;\n /** Impression count */\n impressionCount: number;\n /** Whether clicked */\n clicked: boolean;\n /** Whether closed */\n closed: boolean;\n /** Next eligible timestamp (Unix ms) */\n nextEligibleAt: number;\n}\n\n// ============================================================================\n// SERVICE TYPES\n// ============================================================================\n\n/**\n * Options for fetching campaigns\n */\nexport interface FetchCampaignsOptions {\n /** Filter by placement */\n placement?: AdPlacement;\n /** Filter by status */\n status?: CampaignStatus;\n /** Filter by product */\n productId?: string;\n /** Maximum results */\n limit?: number;\n}\n\n/**\n * Campaign with resolved product data\n */\nexport interface CampaignWithProduct extends Campaign {\n /** Resolved product information */\n product: Product;\n}\n\n// ============================================================================\n// ANALYTICS TYPES\n// ============================================================================\n\n/**\n * Campaign analytics summary\n */\nexport interface CampaignAnalytics {\n campaignId: string;\n campaignName: string;\n productId: string;\n productName: string;\n\n /** Total impressions */\n impressions: number;\n /** Total clicks */\n clicks: number;\n /** Total closes */\n closes: number;\n /** Click-through rate (clicks/impressions) */\n ctr: number;\n /** Close rate (closes/impressions) */\n closeRate: number;\n\n /** Breakdown by platform */\n byPlatform: Record<TargetPlatform, { impressions: number; clicks: number }>;\n /** Breakdown by placement */\n byPlacement: Record<AdPlacement, { impressions: number; clicks: number }>;\n /** Breakdown by project */\n byProject: Record<string, { impressions: number; clicks: number }>;\n /** Daily breakdown */\n byDate: Array<{\n date: string;\n impressions: number;\n clicks: number;\n closes: number;\n }>;\n}\n\n// ============================================================================\n// COMPONENT PROPS\n// ============================================================================\n\n/**\n * Props for small panel variant components\n */\nexport interface SmallPanelProps {\n /** Campaign with product data */\n campaign: CampaignWithProduct;\n /** Called when user clicks CTA */\n onCTAClick?: () => void;\n /** Called when user closes/dismisses the ad */\n onClose?: () => void;\n}\n\n/**\n * Props for large panel variant components\n */\nexport interface LargePanelProps {\n /** Campaign with product data */\n campaign: CampaignWithProduct;\n /** Called when user clicks CTA */\n onCTAClick?: () => void;\n /** Called when user closes/dismisses the ad */\n onClose?: () => void;\n /** Whether to show slide indicator */\n showIndicator?: boolean;\n /** Current slide index (for carousel) */\n currentIndex?: number;\n /** Total number of slides (for carousel) */\n totalCount?: number;\n}\n\n/**\n * Props for ad panel component\n */\nexport interface AdPanelProps {\n placement: AdPlacement;\n variant?: SmallPanelVariant;\n className?: string;\n}\n\n// ============================================================================\n// DEFAULT VALUES\n// ============================================================================\n\n/**\n * Default frequency cap in days\n */\nexport const DEFAULT_FREQUENCY_DAYS = 20;\n\n/**\n * Default campaign priority\n */\nexport const DEFAULT_CAMPAIGN_PRIORITY = 50;\n\n/**\n * Variant display names\n */\nexport const VARIANT_NAMES: Record<AdVariant, string> = {\n small_panel_1: 'Minimal',\n small_panel_2: 'Tagline',\n small_panel_3: 'Features',\n small_panel_4: 'Gradient',\n small_panel_5: 'Card',\n large_slider_1: 'Hero',\n large_slider_2: 'Feature Grid',\n large_slider_3: 'Testimonial',\n large_slider_4: 'Comparison',\n large_slider_5: 'Video Placeholder',\n};\n\n/**\n * Placement display names\n */\nexport const PLACEMENT_NAMES: Record<AdPlacement, string> = {\n popup_slider: 'Extension Popup Slider',\n options_panel: 'Extension Options Panel',\n onetime_modal: 'One-Time Welcome Modal',\n update_modal: 'Update Announcement Modal',\n notification: 'Browser Notification',\n footer_slider: 'Web App Footer Slider',\n sidebar_panel: 'Web App Sidebar Panel',\n home_banner: 'Home Page Banner',\n};\n\n/**\n * Platform display names\n */\nexport const PLATFORM_NAMES: Record<TargetPlatform, string> = {\n web: 'Web App',\n android: 'Android App',\n ios: 'iOS App',\n extension: 'Browser Extension',\n};\n\n// ============================================================================\n// FIREBASE COLLECTION NAMES\n// ============================================================================\n\n/**\n * Collection names for shared features\n */\nexport const COLLECTIONS = {\n CAMPAIGNS: 'zaions_campaigns',\n PRODUCTS: 'zaions_products',\n IMPRESSIONS: 'zaions_impressions',\n CONTACTS: 'zaions_contacts',\n FEATURE_REQUESTS: 'zaions_feature_requests',\n PAYMENT_OPTIONS: 'zaions_payment_options',\n SOCIAL_LINKS: 'zaions_social_links',\n DEVELOPER_INFO: 'zaions_developer_info',\n} as const;\n","/**\n * In-App Notification System Type Definitions\n *\n * Type definitions for the unified notification system including\n * broadcasts, user notifications, templates, and preferences.\n *\n * Architecture:\n * - Broadcasts: Stored in shared-features Firebase (zaions_broadcasts)\n * - User notifications: Stored in consumer project's Firebase\n * - Templates: Stored in shared-features Firebase (zaions_notification_templates)\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { Timestamp } from 'firebase/firestore';\n\n// ============================================================================\n// NOTIFICATION CATEGORIES & TYPES\n// ============================================================================\n\n/**\n * Standard notification categories for grouping and filtering\n */\nexport type NotificationCategory =\n | 'system' // Maintenance, updates, outages\n | 'account' // Welcome, security, profile changes\n | 'activity' // CRUD operations, user actions\n | 'report' // Weekly/monthly summaries\n | 'promotional' // Tips, features, holidays\n | 'social'; // Shares, mentions, comments\n\n/**\n * Visual notification type for styling\n */\nexport type NotificationType =\n | 'info' // General information\n | 'success' // Positive feedback\n | 'warning' // Attention needed\n | 'error' // Something went wrong\n | 'reminder' // Time-based reminder\n | 'milestone' // Achievement/progress\n | 'announcement'; // Important announcement\n\n/**\n * Priority levels for notifications\n */\nexport type NotificationPriority = 'urgent' | 'high' | 'normal' | 'low';\n\n/**\n * Source of the notification\n */\nexport type NotificationSource =\n | 'system' // Auto-generated by system\n | 'event' // Triggered by user action\n | 'broadcast' // Cross-project announcement\n | 'admin'; // Sent by admin\n\n/**\n * Broadcast display variants\n */\nexport type BroadcastVariant = 'banner' | 'modal' | 'toast' | 'bell';\n\n/**\n * Broadcast status\n */\nexport type BroadcastStatus = 'draft' | 'scheduled' | 'active' | 'ended';\n\n/**\n * Target platforms for notifications\n */\nexport type NotificationPlatform = 'web' | 'android' | 'ios';\n\n/**\n * Target audience for broadcasts\n */\nexport type NotificationAudience = 'all' | 'authenticated' | 'anonymous';\n\n// ============================================================================\n// STANDARD EVENT TYPES\n// ============================================================================\n\n/**\n * Standard event types that trigger notifications\n */\nexport type StandardEventType =\n // Account Events\n | 'ACCOUNT_CREATED'\n | 'ACCOUNT_WELCOME'\n | 'NEW_DEVICE_SIGNIN'\n | 'PASSWORD_CHANGED'\n | 'PROFILE_UPDATED'\n | 'ACCOUNT_DELETED'\n // Report Events\n | 'DAILY_SUMMARY'\n | 'WEEKLY_SUMMARY'\n | 'MONTHLY_SUMMARY'\n | 'QUARTERLY_SUMMARY'\n | 'YEARLY_SUMMARY'\n // Promotional Events\n | 'APP_TIP'\n | 'HIDDEN_FEATURE'\n | 'NEW_FEATURE_ANNOUNCEMENT'\n | 'HOLIDAY_GREETING'\n // System Events\n | 'SYSTEM_MAINTENANCE'\n | 'APP_UPDATE_AVAILABLE'\n | 'DATA_EXPORT_READY'\n // Activity Events (Generic CRUD)\n | 'ITEM_CREATED'\n | 'ITEM_UPDATED'\n | 'ITEM_DELETED'\n | 'BULK_OPERATION_COMPLETE';\n\n// ============================================================================\n// BASE NOTIFICATION INTERFACE\n// ============================================================================\n\n/**\n * Base notification fields shared across all notification types\n */\nexport interface BaseNotification {\n /** Unique notification ID */\n id: string;\n /** Notification title */\n title: string;\n /** Notification message/body */\n message: string;\n /** Visual type for styling */\n type: NotificationType;\n /** Category for grouping/filtering */\n category: NotificationCategory;\n /** Whether the notification has been read */\n isRead: boolean;\n /** Whether this notification is important/pinned */\n isImportant?: boolean;\n /** Optional action URL to navigate to */\n actionUrl?: string;\n /** Optional action button text */\n actionText?: string;\n /** When the notification was created */\n createdAt: Date | Timestamp;\n /** When the notification was read */\n readAt?: Date | Timestamp;\n /** Additional metadata for custom use */\n metadata?: Record<string, unknown>;\n}\n\n// ============================================================================\n// USER NOTIFICATION\n// ============================================================================\n\n/**\n * User-specific notification stored in consumer project's Firebase\n * Collection: {prefix}_notifications/{notificationId}\n */\nexport interface UserNotification extends BaseNotification {\n /** User ID this notification belongs to */\n userId: string;\n /** How the notification was generated */\n source: NotificationSource;\n /** Reference to source entity (broadcast ID, event ID, etc.) */\n sourceId?: string;\n /** Event type if triggered by event */\n eventType?: StandardEventType | string;\n /** When the notification expires (auto-delete) */\n expiresAt?: Date | Timestamp;\n}\n\n/**\n * Input for creating a user notification\n */\nexport interface CreateUserNotificationInput {\n userId: string;\n title: string;\n message: string;\n type: NotificationType;\n category: NotificationCategory;\n source: NotificationSource;\n sourceId?: string;\n eventType?: StandardEventType | string;\n isImportant?: boolean;\n actionUrl?: string;\n actionText?: string;\n expiresAt?: Date;\n metadata?: Record<string, unknown>;\n}\n\n// ============================================================================\n// BROADCAST NOTIFICATION\n// ============================================================================\n\n/**\n * Cross-project broadcast notification stored in shared-features Firebase\n * Collection: zaions_broadcasts/{broadcastId}\n */\nexport interface BroadcastNotification extends BaseNotification {\n /** Target project IDs (empty = all projects) */\n targetProjects: string[];\n /** Target platforms */\n targetPlatforms: NotificationPlatform[];\n /** Target audience */\n targetAudience: NotificationAudience;\n /** Broadcast status */\n status: BroadcastStatus;\n /** When the broadcast becomes active */\n startDate: Date | Timestamp;\n /** When the broadcast ends (null = no end) */\n endDate?: Date | Timestamp | null;\n /** Display priority (1-100, higher = more important) */\n priority: number;\n /** Whether user can dismiss this broadcast */\n dismissible: boolean;\n /** How to display this broadcast */\n variant: BroadcastVariant;\n /** Total impressions count */\n impressions: number;\n /** Total clicks count */\n clicks: number;\n /** Admin user ID who created */\n createdBy: string;\n /** Admin user ID who last updated */\n updatedBy?: string;\n}\n\n/**\n * Input for creating a broadcast\n */\nexport interface CreateBroadcastInput {\n title: string;\n message: string;\n type: NotificationType;\n category: NotificationCategory;\n targetProjects: string[];\n targetPlatforms: NotificationPlatform[];\n targetAudience: NotificationAudience;\n startDate: Date;\n endDate?: Date | null;\n priority: number;\n dismissible: boolean;\n variant: BroadcastVariant;\n isImportant?: boolean;\n actionUrl?: string;\n actionText?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Input for updating a broadcast\n */\nexport interface UpdateBroadcastInput extends Partial<CreateBroadcastInput> {\n id: string;\n status?: BroadcastStatus;\n}\n\n// ============================================================================\n// NOTIFICATION TEMPLATE\n// ============================================================================\n\n/**\n * Notification template for standard events\n * Collection: zaions_notification_templates/{templateId}\n */\nexport interface NotificationTemplate {\n /** Template ID */\n id: string;\n /** Template name for admin reference */\n name: string;\n /** Event type this template is for */\n eventType: StandardEventType | string;\n /** Notification category */\n category: NotificationCategory;\n /** Title with placeholders: \"Welcome to {{appName}}, {{userName}}!\" */\n title: string;\n /** Message with placeholders */\n message: string;\n /** List of variable names used in title/message */\n variables: string[];\n /** Visual notification type */\n type: NotificationType;\n /** Whether notifications from this template are important */\n isImportant: boolean;\n /** Default action URL (can use placeholders) */\n actionUrl?: string;\n /** Default action button text */\n actionText?: string;\n /** Whether this template is enabled */\n enabled: boolean;\n /** When template was created */\n createdAt: Date | Timestamp;\n /** When template was last updated */\n updatedAt: Date | Timestamp;\n}\n\n/**\n * Input for creating a template\n */\nexport interface CreateTemplateInput {\n name: string;\n eventType: StandardEventType | string;\n category: NotificationCategory;\n title: string;\n message: string;\n variables: string[];\n type: NotificationType;\n isImportant: boolean;\n actionUrl?: string;\n actionText?: string;\n enabled: boolean;\n}\n\n// ============================================================================\n// USER NOTIFICATION PREFERENCES\n// ============================================================================\n\n/**\n * Category preference settings\n */\nexport interface CategoryPreference {\n /** Show in-app notifications for this category */\n inApp: boolean;\n /** Send push notifications for this category */\n push: boolean;\n /** Send email notifications for this category */\n email: boolean;\n}\n\n/**\n * Quiet hours configuration\n */\nexport interface QuietHoursConfig {\n /** Whether quiet hours are enabled */\n enabled: boolean;\n /** Start time in 24h format: \"22:00\" */\n start: string;\n /** End time in 24h format: \"08:00\" */\n end: string;\n /** User's timezone (IANA format) */\n timezone: string;\n}\n\n/**\n * Email digest configuration\n */\nexport interface EmailDigestConfig {\n /** Whether email digest is enabled */\n enabled: boolean;\n /** Digest frequency */\n frequency: 'daily' | 'weekly' | 'none';\n}\n\n/**\n * Push notification configuration\n */\nexport interface PushConfig {\n /** Whether push is enabled */\n enabled: boolean;\n /** OneSignal player ID */\n playerId?: string;\n /** Web push subscription */\n webPushSubscription?: PushSubscription;\n}\n\n/**\n * User notification preferences\n * Collection: {prefix}_notification_preferences/{userId}\n */\nexport interface NotificationPreferences {\n /** User ID */\n userId: string;\n /** Category-specific preferences */\n categories: Record<NotificationCategory, CategoryPreference>;\n /** Quiet hours settings */\n quietHours: QuietHoursConfig;\n /** Email digest settings */\n emailDigest: EmailDigestConfig;\n /** Push notification settings */\n push: PushConfig;\n /** When preferences were last updated */\n updatedAt: Date | Timestamp;\n}\n\n/**\n * Default notification preferences\n */\nexport const DEFAULT_NOTIFICATION_PREFERENCES: Omit<\n NotificationPreferences,\n 'userId' | 'updatedAt'\n> = {\n categories: {\n system: { inApp: true, push: true, email: false },\n account: { inApp: true, push: true, email: true },\n activity: { inApp: true, push: false, email: false },\n report: { inApp: true, push: false, email: true },\n promotional: { inApp: true, push: false, email: false },\n social: { inApp: true, push: true, email: false },\n },\n quietHours: {\n enabled: false,\n start: '22:00',\n end: '08:00',\n timezone: 'UTC',\n },\n emailDigest: {\n enabled: false,\n frequency: 'none',\n },\n push: {\n enabled: false,\n },\n};\n\n// ============================================================================\n// NOTIFICATION EVENTS\n// ============================================================================\n\n/**\n * Event definition for triggering notifications\n */\nexport interface NotificationEventDefinition {\n /** Event type identifier */\n type: StandardEventType | string;\n /** Event display name */\n name: string;\n /** Event description */\n description: string;\n /** Default notification category */\n category: NotificationCategory;\n /** Default notification type */\n notificationType: NotificationType;\n /** Default priority */\n priority: NotificationPriority;\n /** Available template variables */\n variables: string[];\n /** Whether this event is enabled by default */\n defaultEnabled: boolean;\n}\n\n/**\n * Event trigger payload\n */\nexport interface NotificationEventPayload {\n /** Event type */\n type: StandardEventType | string;\n /** User ID to notify */\n userId: string;\n /** Variable values for template interpolation */\n data: Record<string, string | number | boolean>;\n /** Optional override for notification title */\n titleOverride?: string;\n /** Optional override for notification message */\n messageOverride?: string;\n /** Optional action URL */\n actionUrl?: string;\n /** Optional action text */\n actionText?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n// ============================================================================\n// ANALYTICS TYPES\n// ============================================================================\n\n/**\n * Broadcast analytics summary\n */\nexport interface BroadcastAnalytics {\n broadcastId: string;\n title: string;\n status: BroadcastStatus;\n impressions: number;\n clicks: number;\n dismissals: number;\n ctr: number;\n byPlatform: Record<NotificationPlatform, { impressions: number; clicks: number }>;\n byProject: Record<string, { impressions: number; clicks: number }>;\n byDate: Array<{\n date: string;\n impressions: number;\n clicks: number;\n }>;\n}\n\n/**\n * Overall notification analytics\n */\nexport interface NotificationAnalytics {\n /** Total notifications sent */\n totalSent: number;\n /** Total notifications read */\n totalRead: number;\n /** Total notifications clicked */\n totalClicked: number;\n /** Read rate (read/sent) */\n readRate: number;\n /** Click rate (clicked/sent) */\n clickRate: number;\n /** Breakdown by category */\n byCategory: Record<\n NotificationCategory,\n {\n sent: number;\n read: number;\n clicked: number;\n }\n >;\n /** Breakdown by type */\n byType: Record<\n NotificationType,\n {\n sent: number;\n read: number;\n clicked: number;\n }\n >;\n /** Daily breakdown */\n byDate: Array<{\n date: string;\n sent: number;\n read: number;\n clicked: number;\n }>;\n}\n\n// ============================================================================\n// COMPONENT PROPS\n// ============================================================================\n\n/**\n * Props for NotificationBell component\n */\nexport interface NotificationBellProps {\n /** Additional CSS classes */\n className?: string;\n /** Icon size in pixels */\n size?: number;\n /** Show count badge even when 0 */\n showZeroBadge?: boolean;\n /** Custom onClick handler (default: toggle panel) */\n onClick?: () => void;\n}\n\n/**\n * Props for NotificationPanel component\n */\nexport interface NotificationPanelProps {\n /** Whether the panel is open */\n isOpen: boolean;\n /** Called when panel should close */\n onClose: () => void;\n /** Maximum height for the panel */\n maxHeight?: string;\n /** Whether to show the filter tabs */\n showFilters?: boolean;\n /** Custom empty state component */\n emptyStateComponent?: React.ReactNode;\n}\n\n/**\n * Props for NotificationCard component\n */\nexport interface NotificationCardProps {\n /** The notification to display */\n notification: UserNotification;\n /** Called when notification is clicked */\n onClick?: () => void;\n /** Called when delete button is clicked */\n onDelete?: () => void;\n /** Whether to show delete button */\n showDelete?: boolean;\n /** Whether to show timestamp */\n showTimestamp?: boolean;\n}\n\n/**\n * Props for BroadcastBanner component\n */\nexport interface BroadcastBannerProps {\n /** The broadcast to display */\n broadcast: BroadcastNotification;\n /** Called when CTA is clicked */\n onActionClick?: () => void;\n /** Called when banner is dismissed */\n onDismiss?: () => void;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Props for AnnouncementModal component\n */\nexport interface AnnouncementModalProps {\n /** The broadcast to display */\n broadcast: BroadcastNotification;\n /** Whether the modal is open */\n isOpen: boolean;\n /** Called when modal should close */\n onClose: () => void;\n /** Called when CTA is clicked */\n onActionClick?: () => void;\n}\n\n/**\n * Props for NotificationPreferences component\n */\nexport interface NotificationPreferencesProps {\n /** Current preferences */\n preferences: NotificationPreferences;\n /** Called when preferences change */\n onPreferencesChange: (preferences: NotificationPreferences) => void;\n /** Whether saving is in progress */\n isSaving?: boolean;\n /** Categories to show (default: all) */\n visibleCategories?: NotificationCategory[];\n /** Whether to show push settings */\n showPushSettings?: boolean;\n /** Whether to show email settings */\n showEmailSettings?: boolean;\n /** Whether to show quiet hours settings */\n showQuietHours?: boolean;\n}\n\n// ============================================================================\n// HOOK RETURN TYPES\n// ============================================================================\n\n/**\n * Return type for useNotifications hook\n */\nexport interface UseNotificationsReturn {\n /** List of notifications */\n notifications: UserNotification[];\n /** Number of unread notifications */\n unreadCount: number;\n /** Whether notifications are loading */\n isLoading: boolean;\n /** Error if any */\n error: Error | null;\n /** Mark a notification as read */\n markAsRead: (notificationId: string) => Promise<void>;\n /** Mark all notifications as read */\n markAllAsRead: () => Promise<void>;\n /** Delete a notification */\n deleteNotification: (notificationId: string) => Promise<void>;\n /** Clear all notifications */\n clearAll: () => Promise<void>;\n /** Refresh notifications */\n refresh: () => Promise<void>;\n}\n\n/**\n * Return type for useBroadcasts hook\n */\nexport interface UseBroadcastsReturn {\n /** List of active broadcasts */\n broadcasts: BroadcastNotification[];\n /** Whether broadcasts are loading */\n isLoading: boolean;\n /** Error if any */\n error: Error | null;\n /** Dismiss a broadcast */\n dismissBroadcast: (broadcastId: string) => void;\n /** Track broadcast impression */\n trackImpression: (broadcastId: string) => void;\n /** Track broadcast click */\n trackClick: (broadcastId: string) => void;\n /** Check if broadcast was dismissed */\n isDismissed: (broadcastId: string) => boolean;\n /** Refresh broadcasts from cache */\n refresh: () => Promise<void>;\n}\n\n/**\n * Return type for useNotificationEvents hook\n */\nexport interface UseNotificationEventsReturn {\n /** Trigger a notification event */\n trigger: (payload: NotificationEventPayload) => Promise<void>;\n /** Trigger account created event */\n triggerAccountCreated: (data: { userName: string; email: string }) => Promise<void>;\n /** Trigger new device signin event */\n triggerNewDeviceSignin: (data: { deviceName: string; location?: string }) => Promise<void>;\n /** Trigger password changed event */\n triggerPasswordChanged: () => Promise<void>;\n /** Trigger profile updated event */\n triggerProfileUpdated: (data: { changes: string[] }) => Promise<void>;\n /** Trigger CRUD event */\n triggerCrudEvent: (data: {\n action: 'created' | 'updated' | 'deleted';\n entityType: string;\n entityName: string;\n }) => Promise<void>;\n /** Trigger app tip event */\n triggerAppTip: (data: { tipId: string; title: string; body: string }) => Promise<void>;\n}\n\n// ============================================================================\n// SERVICE TYPES\n// ============================================================================\n\n/**\n * Options for fetching broadcasts\n */\nexport interface FetchBroadcastsOptions {\n /** Filter by project ID */\n projectId?: string;\n /** Filter by platform */\n platform?: NotificationPlatform;\n /** Filter by variant */\n variant?: BroadcastVariant;\n /** Filter by status */\n status?: BroadcastStatus;\n /** Maximum results */\n limit?: number;\n}\n\n/**\n * Options for fetching user notifications\n */\nexport interface FetchNotificationsOptions {\n /** User ID */\n userId: string;\n /** Filter by category */\n category?: NotificationCategory;\n /** Filter by read status */\n isRead?: boolean;\n /** Maximum results */\n limit?: number;\n /** Offset for pagination */\n offset?: number;\n /** Start date filter */\n startDate?: Date;\n /** End date filter */\n endDate?: Date;\n}\n\n// ============================================================================\n// COLLECTION NAMES\n// ============================================================================\n\n/**\n * Shared collections (stored in shared-features Firebase)\n */\nexport const NOTIFICATION_COLLECTIONS = {\n /** Cross-project broadcasts */\n BROADCASTS: 'zaions_broadcasts',\n /** Notification templates */\n TEMPLATES: 'zaions_notification_templates',\n /** Broadcast events/analytics */\n BROADCAST_EVENTS: 'zaions_broadcast_events',\n} as const;\n\n/**\n * Get consumer project collection names\n * @param prefix - Project prefix (e.g., 'pp' for pregnancy-pal)\n */\nexport const getNotificationCollections = (prefix: string) =>\n ({\n /** User notifications */\n NOTIFICATIONS: `${prefix}_notifications`,\n /** User notification preferences */\n PREFERENCES: `${prefix}_notification_preferences`,\n /** Push subscriptions */\n PUSH_SUBSCRIPTIONS: `${prefix}_push_subscriptions`,\n }) as const;\n\n// ============================================================================\n// DISPLAY NAMES & CONSTANTS\n// ============================================================================\n\n/**\n * Category display names\n */\nexport const CATEGORY_NAMES: Record<NotificationCategory, string> = {\n system: 'System',\n account: 'Account',\n activity: 'Activity',\n report: 'Reports',\n promotional: 'Tips & Updates',\n social: 'Social',\n};\n\n/**\n * Category descriptions\n */\nexport const CATEGORY_DESCRIPTIONS: Record<NotificationCategory, string> = {\n system: 'Maintenance updates, outages, and system alerts',\n account: 'Welcome messages, security alerts, and profile changes',\n activity: 'Updates about your actions and changes',\n report: 'Weekly and monthly summaries of your activity',\n promotional: 'Tips, new features, and holiday greetings',\n social: 'Mentions, shares, and comments from others',\n};\n\n/**\n * Notification type display names\n */\nexport const TYPE_NAMES: Record<NotificationType, string> = {\n info: 'Information',\n success: 'Success',\n warning: 'Warning',\n error: 'Error',\n reminder: 'Reminder',\n milestone: 'Milestone',\n announcement: 'Announcement',\n};\n\n/**\n * Priority display names\n */\nexport const PRIORITY_NAMES: Record<NotificationPriority, string> = {\n urgent: 'Urgent',\n high: 'High',\n normal: 'Normal',\n low: 'Low',\n};\n\n/**\n * Event type display names\n */\nexport const EVENT_TYPE_NAMES: Record<StandardEventType, string> = {\n ACCOUNT_CREATED: 'Account Created',\n ACCOUNT_WELCOME: 'Welcome Message',\n NEW_DEVICE_SIGNIN: 'New Device Sign-in',\n PASSWORD_CHANGED: 'Password Changed',\n PROFILE_UPDATED: 'Profile Updated',\n ACCOUNT_DELETED: 'Account Deleted',\n DAILY_SUMMARY: 'Daily Summary',\n WEEKLY_SUMMARY: 'Weekly Summary',\n MONTHLY_SUMMARY: 'Monthly Summary',\n QUARTERLY_SUMMARY: 'Quarterly Summary',\n YEARLY_SUMMARY: 'Yearly Summary',\n APP_TIP: 'App Tip',\n HIDDEN_FEATURE: 'Hidden Feature',\n NEW_FEATURE_ANNOUNCEMENT: 'New Feature',\n HOLIDAY_GREETING: 'Holiday Greeting',\n SYSTEM_MAINTENANCE: 'System Maintenance',\n APP_UPDATE_AVAILABLE: 'App Update Available',\n DATA_EXPORT_READY: 'Data Export Ready',\n ITEM_CREATED: 'Item Created',\n ITEM_UPDATED: 'Item Updated',\n ITEM_DELETED: 'Item Deleted',\n BULK_OPERATION_COMPLETE: 'Bulk Operation Complete',\n};\n\n/**\n * Default broadcast priority\n */\nexport const DEFAULT_BROADCAST_PRIORITY = 50;\n\n/**\n * Maximum notification title length\n */\nexport const MAX_NOTIFICATION_TITLE_LENGTH = 100;\n\n/**\n * Maximum notification message length\n */\nexport const MAX_NOTIFICATION_MESSAGE_LENGTH = 500;\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { C, a, D, b, c, d, e, F, f, P, g, h, S, i, j, k } from "../commonFeatures-XJ9fuxg_.js";
1
+ import { C, a, D, b, c, d, e, F, f, P, g, h, S, i, j, k, s } from "../featureFlags-CqKSOF7q.js";
2
2
  const DEFAULT_FREQUENCY_DAYS = 20;
3
3
  const DEFAULT_CAMPAIGN_PRIORITY = 50;
4
4
  const VARIANT_NAMES = {
@@ -169,6 +169,7 @@ export {
169
169
  k as SOCIAL_PLATFORM_NAMES,
170
170
  TYPE_NAMES,
171
171
  VARIANT_NAMES,
172
- getNotificationCollections
172
+ getNotificationCollections,
173
+ s as setCommonFeatureCollectionNames
173
174
  };
174
175
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  const react = require("react");
3
- const commonFeatures = require("./commonFeatures-B9NKYWuL.cjs");
3
+ const commonFeatures = require("./commonFeatures-DTaIBhdj.cjs");
4
4
  function useCampaigns(options) {
5
5
  const {
6
6
  placement,
@@ -567,4 +567,4 @@ exports.useSkills = useSkills;
567
567
  exports.useSocialLinks = useSocialLinks;
568
568
  exports.useTestimonials = useTestimonials;
569
569
  exports.useUpdateAdModal = useUpdateAdModal;
570
- //# sourceMappingURL=useCommonFeatures-CwdJmhZE.cjs.map
570
+ //# sourceMappingURL=useCommonFeatures-B0H0o03A.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCommonFeatures-CwdJmhZE.cjs","sources":["../src/hooks/useCampaigns.ts","../src/hooks/useCommonFeatures.ts"],"sourcesContent":["/**\n * useCampaigns Hook\n *\n * React hook for fetching and displaying campaigns in consumer projects.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { fetchActiveCampaigns, clearCampaignsCache } from '../services/campaigns';\nimport {\n trackImpression,\n trackClick,\n trackClose,\n isEligibleForCampaign,\n} from '../services/analytics';\nimport { isInitialized } from '../firebase/config';\nimport type {\n CampaignWithProduct,\n AdPlacement,\n SmallPanelVariant,\n LargePanelVariant,\n} from '../types/campaigns';\n\nexport interface UseCampaignsOptions {\n /** Placement to fetch campaigns for */\n placement: AdPlacement;\n /** Maximum number of campaigns to fetch */\n maxCampaigns?: number;\n /** Whether to auto-fetch on mount (default: true) */\n autoFetch?: boolean;\n /** Default variant for small placements */\n defaultSmallVariant?: SmallPanelVariant;\n /** Default variant for large placements */\n defaultLargeVariant?: LargePanelVariant;\n}\n\nexport interface UseCampaignsResult {\n /** List of eligible campaigns with product data */\n campaigns: CampaignWithProduct[];\n /** Single campaign (first eligible) - convenience accessor */\n campaign: CampaignWithProduct | null;\n /** Whether campaigns are being fetched */\n loading: boolean;\n /** Error message if fetch failed */\n error: string | null;\n /** Refetch campaigns */\n refetch: () => Promise<void>;\n /** Record impression for a campaign */\n recordImpression: (campaign: CampaignWithProduct) => Promise<void>;\n /** Record click for a campaign */\n recordClick: (campaign: CampaignWithProduct) => Promise<void>;\n /** Record close/dismiss for a campaign */\n recordClose: (campaign: CampaignWithProduct) => Promise<void>;\n}\n\n/**\n * Hook to fetch and manage campaigns for a specific placement\n *\n * @example\n * ```tsx\n * const { campaigns, loading, recordImpression, recordClick } = useCampaigns({\n * placement: 'footer_slider',\n * maxCampaigns: 5,\n * });\n *\n * if (loading) return <Spinner />;\n *\n * return (\n * <AdSlider\n * campaigns={campaigns}\n * onImpression={recordImpression}\n * onClick={recordClick}\n * />\n * );\n * ```\n */\nexport function useCampaigns(options: UseCampaignsOptions): UseCampaignsResult {\n const {\n placement,\n maxCampaigns = 5,\n autoFetch = true,\n defaultSmallVariant = 'small_panel_2',\n defaultLargeVariant: _defaultLargeVariant = 'large_slider_1',\n } = options;\n // Note: _defaultLargeVariant reserved for large variant components\n\n const [campaigns, setCampaigns] = useState<CampaignWithProduct[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n\n const fetchCampaigns = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const allCampaigns = await fetchActiveCampaigns(placement);\n\n // Filter by frequency capping\n const eligibleCampaigns: CampaignWithProduct[] = [];\n\n for (const campaign of allCampaigns) {\n const eligible = await isEligibleForCampaign(\n campaign.id,\n campaign.frequencyDays\n );\n if (eligible) {\n eligibleCampaigns.push(campaign);\n if (eligibleCampaigns.length >= maxCampaigns) break;\n }\n }\n\n setCampaigns(eligibleCampaigns);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to fetch campaigns';\n setError(message);\n console.error('[shared-features] Error fetching campaigns:', err);\n } finally {\n setLoading(false);\n }\n }, [placement, maxCampaigns]);\n\n // Auto-fetch on mount\n useEffect(() => {\n if (autoFetch) {\n fetchCampaigns();\n }\n }, [autoFetch, fetchCampaigns]);\n\n // Record impression\n const handleRecordImpression = useCallback(\n async (campaign: CampaignWithProduct) => {\n const variant = campaign.variant.startsWith('small_')\n ? campaign.variant\n : campaign.variant.startsWith('large_')\n ? campaign.variant\n : defaultSmallVariant;\n\n await trackImpression(\n campaign.id,\n campaign.productId,\n placement,\n variant,\n campaign.frequencyDays\n );\n },\n [placement, defaultSmallVariant]\n );\n\n // Record click\n const handleRecordClick = useCallback(\n async (campaign: CampaignWithProduct) => {\n const variant = campaign.variant;\n await trackClick(campaign.id, campaign.productId, placement, variant);\n },\n [placement]\n );\n\n // Record close\n const handleRecordClose = useCallback(\n async (campaign: CampaignWithProduct) => {\n const variant = campaign.variant;\n await trackClose(campaign.id, campaign.productId, placement, variant);\n },\n [placement]\n );\n\n // Refetch with cache clear\n const refetch = useCallback(async () => {\n clearCampaignsCache();\n await fetchCampaigns();\n }, [fetchCampaigns]);\n\n return {\n campaigns,\n campaign: campaigns[0] || null,\n loading,\n error,\n refetch,\n recordImpression: handleRecordImpression,\n recordClick: handleRecordClick,\n recordClose: handleRecordClose,\n };\n}\n\n/**\n * Hook to fetch a single campaign for a placement\n * Convenience wrapper around useCampaigns\n */\nexport function useCampaign(options: Omit<UseCampaignsOptions, 'maxCampaigns'>) {\n return useCampaigns({ ...options, maxCampaigns: 1 });\n}\n\n// Storage keys for modal hooks\nconst STORAGE_KEYS = {\n oneTimeShown: 'shared_features_onetime_ad_shown',\n appVersion: 'shared_features_app_version',\n} as const;\n\n/**\n * Hook to manage one-time ad modal visibility\n * Shows modal on first visit, then remembers user has seen it\n *\n * @example\n * ```tsx\n * const { shouldShow, markAsShown } = useOneTimeAdModal();\n *\n * if (shouldShow) {\n * return <AdModal onClose={markAsShown} />;\n * }\n * ```\n */\nexport function useOneTimeAdModal() {\n const [hasShown, setHasShown] = useState(false);\n const [shouldShow, setShouldShow] = useState(false);\n\n useEffect(() => {\n // Check if modal has been shown before\n const hasSeenModal = localStorage.getItem(STORAGE_KEYS.oneTimeShown);\n if (!hasSeenModal) {\n setShouldShow(true);\n }\n }, []);\n\n const markAsShown = useCallback(() => {\n localStorage.setItem(STORAGE_KEYS.oneTimeShown, 'true');\n setHasShown(true);\n setShouldShow(false);\n }, []);\n\n return {\n /** Whether the modal should be displayed */\n shouldShow: shouldShow && !hasShown,\n /** Mark the modal as shown (call when user dismisses) */\n markAsShown,\n };\n}\n\n/**\n * Hook to manage update ad modal visibility\n * Shows modal when app version changes\n *\n * @param currentVersion - Current app version (defaults to VITE_APP_VERSION env var or '1.0.0')\n *\n * @example\n * ```tsx\n * const { shouldShow, currentVersion, markAsShown } = useUpdateAdModal();\n *\n * if (shouldShow) {\n * return <AdUpdateModal version={currentVersion} onClose={markAsShown} />;\n * }\n * ```\n */\nexport function useUpdateAdModal(currentVersion?: string) {\n const version = currentVersion || import.meta.env.VITE_APP_VERSION || '1.0.0';\n const [shouldShow, setShouldShow] = useState(false);\n const [previousVersion, setPreviousVersion] = useState<string | null>(null);\n\n useEffect(() => {\n const storedVersion = localStorage.getItem(STORAGE_KEYS.appVersion);\n\n if (!storedVersion) {\n // First time user - store version but don't show update modal\n localStorage.setItem(STORAGE_KEYS.appVersion, version);\n } else if (storedVersion !== version) {\n // Version changed - show update modal\n setShouldShow(true);\n setPreviousVersion(storedVersion);\n }\n }, [version]);\n\n const markAsShown = useCallback(() => {\n localStorage.setItem(STORAGE_KEYS.appVersion, version);\n setShouldShow(false);\n }, [version]);\n\n return {\n /** Whether the modal should be displayed */\n shouldShow,\n /** Previous app version (before update) */\n previousVersion,\n /** Current app version */\n currentVersion: version,\n /** Mark the modal as shown (call when user dismisses) */\n markAsShown,\n };\n}\n","/**\n * Common Features Hooks\n *\n * React hooks for fetching and using common features in consumer projects.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport { isInitialized } from '../firebase/config';\nimport {\n fetchContactInfo,\n subscribeToContactInfo,\n clearContactInfoCache,\n fetchDeveloperInfo,\n subscribeToDeveloperInfo,\n clearDeveloperInfoCache,\n fetchAddressInfo,\n clearAddressInfoCache,\n fetchSocialLinks,\n clearSocialLinksCache,\n fetchPaymentOptions,\n clearPaymentOptionsCache,\n fetchServices,\n clearServicesCache,\n fetchSkills,\n clearSkillsCache,\n fetchTestimonials,\n clearTestimonialsCache,\n fetchProjects,\n fetchProjectBySlug,\n clearProjectsCache,\n} from '../services/commonFeatures';\nimport type {\n ContactInfo,\n DeveloperInfo,\n AddressInfo,\n SocialLink,\n PaymentOption,\n Service,\n Skill,\n Testimonial,\n Project,\n UseCommonFeatureOptions,\n UseCommonFeatureResult,\n UseCommonFeaturesListResult,\n FetchSocialLinksOptions,\n FetchServicesOptions,\n FetchSkillsOptions,\n FetchTestimonialsOptions,\n FetchPaymentOptionsOptions,\n FetchProjectsOptions,\n} from '../types/commonFeatures';\n\n// ============================================================================\n// CONTACT INFO\n// ============================================================================\n\n/**\n * Hook to fetch contact information\n *\n * @example\n * ```tsx\n * const { data: contact, loading } = useContactInfo();\n *\n * if (loading) return <Spinner />;\n * if (!contact) return null;\n *\n * return <a href={`mailto:${contact.email}`}>{contact.email}</a>;\n * ```\n */\nexport function useContactInfo(\n options: UseCommonFeatureOptions = {}\n): UseCommonFeatureResult<ContactInfo> {\n const { autoFetch = true, realtime = false } = options;\n\n const [data, setData] = useState<ContactInfo | null>(null);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchContactInfo();\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch contact info');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, []);\n\n const refetch = useCallback(async () => {\n clearContactInfoCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n\n if (realtime && isInitialized()) {\n const unsubscribe = subscribeToContactInfo((result) => {\n if (mountedRef.current) {\n setData(result);\n setLoading(false);\n }\n });\n return () => {\n mountedRef.current = false;\n unsubscribe();\n };\n }\n\n if (autoFetch) fetch();\n\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, realtime, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// DEVELOPER INFO\n// ============================================================================\n\n/**\n * Hook to fetch developer information\n *\n * @example\n * ```tsx\n * const { data: developer, loading } = useDeveloperInfo();\n *\n * if (loading) return <Spinner />;\n * if (!developer) return null;\n *\n * return (\n * <div>\n * <h1>{developer.name}</h1>\n * <p>{developer.title}</p>\n * </div>\n * );\n * ```\n */\nexport function useDeveloperInfo(\n options: UseCommonFeatureOptions = {}\n): UseCommonFeatureResult<DeveloperInfo> {\n const { autoFetch = true, realtime = false } = options;\n\n const [data, setData] = useState<DeveloperInfo | null>(null);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchDeveloperInfo();\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch developer info');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, []);\n\n const refetch = useCallback(async () => {\n clearDeveloperInfoCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n\n if (realtime && isInitialized()) {\n const unsubscribe = subscribeToDeveloperInfo((result) => {\n if (mountedRef.current) {\n setData(result);\n setLoading(false);\n }\n });\n return () => {\n mountedRef.current = false;\n unsubscribe();\n };\n }\n\n if (autoFetch) fetch();\n\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, realtime, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// ADDRESS INFO\n// ============================================================================\n\n/**\n * Hook to fetch address information\n */\nexport function useAddressInfo(\n options: UseCommonFeatureOptions = {}\n): UseCommonFeatureResult<AddressInfo> {\n const { autoFetch = true } = options;\n\n const [data, setData] = useState<AddressInfo | null>(null);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchAddressInfo();\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch address info');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, []);\n\n const refetch = useCallback(async () => {\n clearAddressInfoCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// SOCIAL LINKS\n// ============================================================================\n\n/**\n * Hook to fetch social links\n *\n * @example\n * ```tsx\n * const { data: links, loading } = useSocialLinks({ showIn: ['footer'] });\n *\n * if (loading) return <Spinner />;\n *\n * return (\n * <div>\n * {links.map(link => (\n * <a key={link.id} href={link.url}>{link.platform}</a>\n * ))}\n * </div>\n * );\n * ```\n */\nexport function useSocialLinks(\n options: FetchSocialLinksOptions & UseCommonFeatureOptions = {}\n): UseCommonFeaturesListResult<SocialLink> {\n const { autoFetch = true, showIn, activeOnly } = options;\n\n const [data, setData] = useState<SocialLink[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchSocialLinks({ showIn, activeOnly });\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch social links');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [showIn, activeOnly]);\n\n const refetch = useCallback(async () => {\n clearSocialLinksCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// PAYMENT OPTIONS\n// ============================================================================\n\n/**\n * Hook to fetch payment options\n */\nexport function usePaymentOptions(\n options: FetchPaymentOptionsOptions & UseCommonFeatureOptions = {}\n): UseCommonFeaturesListResult<PaymentOption> {\n const { autoFetch = true, activeOnly, type } = options;\n\n const [data, setData] = useState<PaymentOption[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchPaymentOptions({ activeOnly, type });\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch payment options');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [activeOnly, type]);\n\n const refetch = useCallback(async () => {\n clearPaymentOptionsCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// SERVICES\n// ============================================================================\n\n/**\n * Hook to fetch services\n */\nexport function useServices(\n options: FetchServicesOptions & UseCommonFeatureOptions = {}\n): UseCommonFeaturesListResult<Service> {\n const { autoFetch = true, category, activeOnly, featuredOnly } = options;\n\n const [data, setData] = useState<Service[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchServices({ category, activeOnly, featuredOnly });\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch services');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [category, activeOnly, featuredOnly]);\n\n const refetch = useCallback(async () => {\n clearServicesCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// SKILLS\n// ============================================================================\n\n/**\n * Hook to fetch skills\n */\nexport function useSkills(\n options: FetchSkillsOptions & UseCommonFeatureOptions = {}\n): UseCommonFeaturesListResult<Skill> {\n const { autoFetch = true, category, activeOnly, featuredOnly } = options;\n\n const [data, setData] = useState<Skill[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchSkills({ category, activeOnly, featuredOnly });\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch skills');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [category, activeOnly, featuredOnly]);\n\n const refetch = useCallback(async () => {\n clearSkillsCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// TESTIMONIALS\n// ============================================================================\n\n/**\n * Hook to fetch testimonials\n */\nexport function useTestimonials(\n options: FetchTestimonialsOptions & UseCommonFeatureOptions = {}\n): UseCommonFeaturesListResult<Testimonial> {\n const { autoFetch = true, activeOnly, featuredOnly, limit } = options;\n\n const [data, setData] = useState<Testimonial[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchTestimonials({ activeOnly, featuredOnly, limit });\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch testimonials');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [activeOnly, featuredOnly, limit]);\n\n const refetch = useCallback(async () => {\n clearTestimonialsCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// PROJECTS\n// ============================================================================\n\n/**\n * Hook to fetch projects\n *\n * @example\n * ```tsx\n * const { data: projects, loading } = useProjects({ featuredOnly: true });\n *\n * if (loading) return <Spinner />;\n *\n * return (\n * <div>\n * {projects.map(project => (\n * <div key={project.id}>\n * <h2>{project.title}</h2>\n * <p>{project.description}</p>\n * </div>\n * ))}\n * </div>\n * );\n * ```\n */\nexport function useProjects(\n options: FetchProjectsOptions & UseCommonFeatureOptions = {}\n): UseCommonFeaturesListResult<Project> {\n const { autoFetch = true, category, status, activeOnly, featuredOnly, limit } = options;\n\n const [data, setData] = useState<Project[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchProjects({ category, status, activeOnly, featuredOnly, limit });\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch projects');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [category, status, activeOnly, featuredOnly, limit]);\n\n const refetch = useCallback(async () => {\n clearProjectsCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n/**\n * Hook to fetch a single project by slug\n */\nexport function useProject(\n slug: string,\n options: { autoFetch?: boolean } = {}\n): UseCommonFeatureResult<Project> {\n const { autoFetch = true } = options;\n\n const [data, setData] = useState<Project | null>(null);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n if (!slug) {\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchProjectBySlug(slug);\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch project');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [slug]);\n\n const refetch = useCallback(async () => {\n clearProjectsCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n"],"names":["useState","useCallback","isInitialized","fetchActiveCampaigns","isEligibleForCampaign","useEffect","trackImpression","trackClick","trackClose","clearCampaignsCache","useRef","fetchContactInfo","clearContactInfoCache","subscribeToContactInfo","fetchDeveloperInfo","clearDeveloperInfoCache","subscribeToDeveloperInfo","fetchAddressInfo","clearAddressInfoCache","fetchSocialLinks","clearSocialLinksCache","fetchPaymentOptions","clearPaymentOptionsCache","fetchServices","clearServicesCache","fetchSkills","clearSkillsCache","fetchTestimonials","clearTestimonialsCache","fetchProjects","clearProjectsCache","fetchProjectBySlug"],"mappings":"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"}
1
+ {"version":3,"file":"useCommonFeatures-B0H0o03A.cjs","sources":["../src/hooks/useCampaigns.ts","../src/hooks/useCommonFeatures.ts"],"sourcesContent":["/**\n * useCampaigns Hook\n *\n * React hook for fetching and displaying campaigns in consumer projects.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { fetchActiveCampaigns, clearCampaignsCache } from '../services/campaigns';\nimport {\n trackImpression,\n trackClick,\n trackClose,\n isEligibleForCampaign,\n} from '../services/analytics';\nimport { isInitialized } from '../firebase/config';\nimport type {\n CampaignWithProduct,\n AdPlacement,\n SmallPanelVariant,\n LargePanelVariant,\n} from '../types/campaigns';\n\nexport interface UseCampaignsOptions {\n /** Placement to fetch campaigns for */\n placement: AdPlacement;\n /** Maximum number of campaigns to fetch */\n maxCampaigns?: number;\n /** Whether to auto-fetch on mount (default: true) */\n autoFetch?: boolean;\n /** Default variant for small placements */\n defaultSmallVariant?: SmallPanelVariant;\n /** Default variant for large placements */\n defaultLargeVariant?: LargePanelVariant;\n}\n\nexport interface UseCampaignsResult {\n /** List of eligible campaigns with product data */\n campaigns: CampaignWithProduct[];\n /** Single campaign (first eligible) - convenience accessor */\n campaign: CampaignWithProduct | null;\n /** Whether campaigns are being fetched */\n loading: boolean;\n /** Error message if fetch failed */\n error: string | null;\n /** Refetch campaigns */\n refetch: () => Promise<void>;\n /** Record impression for a campaign */\n recordImpression: (campaign: CampaignWithProduct) => Promise<void>;\n /** Record click for a campaign */\n recordClick: (campaign: CampaignWithProduct) => Promise<void>;\n /** Record close/dismiss for a campaign */\n recordClose: (campaign: CampaignWithProduct) => Promise<void>;\n}\n\n/**\n * Hook to fetch and manage campaigns for a specific placement\n *\n * @example\n * ```tsx\n * const { campaigns, loading, recordImpression, recordClick } = useCampaigns({\n * placement: 'footer_slider',\n * maxCampaigns: 5,\n * });\n *\n * if (loading) return <Spinner />;\n *\n * return (\n * <AdSlider\n * campaigns={campaigns}\n * onImpression={recordImpression}\n * onClick={recordClick}\n * />\n * );\n * ```\n */\nexport function useCampaigns(options: UseCampaignsOptions): UseCampaignsResult {\n const {\n placement,\n maxCampaigns = 5,\n autoFetch = true,\n defaultSmallVariant = 'small_panel_2',\n defaultLargeVariant: _defaultLargeVariant = 'large_slider_1',\n } = options;\n // Note: _defaultLargeVariant reserved for large variant components\n\n const [campaigns, setCampaigns] = useState<CampaignWithProduct[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n\n const fetchCampaigns = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const allCampaigns = await fetchActiveCampaigns(placement);\n\n // Filter by frequency capping\n const eligibleCampaigns: CampaignWithProduct[] = [];\n\n for (const campaign of allCampaigns) {\n const eligible = await isEligibleForCampaign(\n campaign.id,\n campaign.frequencyDays\n );\n if (eligible) {\n eligibleCampaigns.push(campaign);\n if (eligibleCampaigns.length >= maxCampaigns) break;\n }\n }\n\n setCampaigns(eligibleCampaigns);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to fetch campaigns';\n setError(message);\n console.error('[shared-features] Error fetching campaigns:', err);\n } finally {\n setLoading(false);\n }\n }, [placement, maxCampaigns]);\n\n // Auto-fetch on mount\n useEffect(() => {\n if (autoFetch) {\n fetchCampaigns();\n }\n }, [autoFetch, fetchCampaigns]);\n\n // Record impression\n const handleRecordImpression = useCallback(\n async (campaign: CampaignWithProduct) => {\n const variant = campaign.variant.startsWith('small_')\n ? campaign.variant\n : campaign.variant.startsWith('large_')\n ? campaign.variant\n : defaultSmallVariant;\n\n await trackImpression(\n campaign.id,\n campaign.productId,\n placement,\n variant,\n campaign.frequencyDays\n );\n },\n [placement, defaultSmallVariant]\n );\n\n // Record click\n const handleRecordClick = useCallback(\n async (campaign: CampaignWithProduct) => {\n const variant = campaign.variant;\n await trackClick(campaign.id, campaign.productId, placement, variant);\n },\n [placement]\n );\n\n // Record close\n const handleRecordClose = useCallback(\n async (campaign: CampaignWithProduct) => {\n const variant = campaign.variant;\n await trackClose(campaign.id, campaign.productId, placement, variant);\n },\n [placement]\n );\n\n // Refetch with cache clear\n const refetch = useCallback(async () => {\n clearCampaignsCache();\n await fetchCampaigns();\n }, [fetchCampaigns]);\n\n return {\n campaigns,\n campaign: campaigns[0] || null,\n loading,\n error,\n refetch,\n recordImpression: handleRecordImpression,\n recordClick: handleRecordClick,\n recordClose: handleRecordClose,\n };\n}\n\n/**\n * Hook to fetch a single campaign for a placement\n * Convenience wrapper around useCampaigns\n */\nexport function useCampaign(options: Omit<UseCampaignsOptions, 'maxCampaigns'>) {\n return useCampaigns({ ...options, maxCampaigns: 1 });\n}\n\n// Storage keys for modal hooks\nconst STORAGE_KEYS = {\n oneTimeShown: 'shared_features_onetime_ad_shown',\n appVersion: 'shared_features_app_version',\n} as const;\n\n/**\n * Hook to manage one-time ad modal visibility\n * Shows modal on first visit, then remembers user has seen it\n *\n * @example\n * ```tsx\n * const { shouldShow, markAsShown } = useOneTimeAdModal();\n *\n * if (shouldShow) {\n * return <AdModal onClose={markAsShown} />;\n * }\n * ```\n */\nexport function useOneTimeAdModal() {\n const [hasShown, setHasShown] = useState(false);\n const [shouldShow, setShouldShow] = useState(false);\n\n useEffect(() => {\n // Check if modal has been shown before\n const hasSeenModal = localStorage.getItem(STORAGE_KEYS.oneTimeShown);\n if (!hasSeenModal) {\n setShouldShow(true);\n }\n }, []);\n\n const markAsShown = useCallback(() => {\n localStorage.setItem(STORAGE_KEYS.oneTimeShown, 'true');\n setHasShown(true);\n setShouldShow(false);\n }, []);\n\n return {\n /** Whether the modal should be displayed */\n shouldShow: shouldShow && !hasShown,\n /** Mark the modal as shown (call when user dismisses) */\n markAsShown,\n };\n}\n\n/**\n * Hook to manage update ad modal visibility\n * Shows modal when app version changes\n *\n * @param currentVersion - Current app version (defaults to VITE_APP_VERSION env var or '1.0.0')\n *\n * @example\n * ```tsx\n * const { shouldShow, currentVersion, markAsShown } = useUpdateAdModal();\n *\n * if (shouldShow) {\n * return <AdUpdateModal version={currentVersion} onClose={markAsShown} />;\n * }\n * ```\n */\nexport function useUpdateAdModal(currentVersion?: string) {\n const version = currentVersion || import.meta.env.VITE_APP_VERSION || '1.0.0';\n const [shouldShow, setShouldShow] = useState(false);\n const [previousVersion, setPreviousVersion] = useState<string | null>(null);\n\n useEffect(() => {\n const storedVersion = localStorage.getItem(STORAGE_KEYS.appVersion);\n\n if (!storedVersion) {\n // First time user - store version but don't show update modal\n localStorage.setItem(STORAGE_KEYS.appVersion, version);\n } else if (storedVersion !== version) {\n // Version changed - show update modal\n setShouldShow(true);\n setPreviousVersion(storedVersion);\n }\n }, [version]);\n\n const markAsShown = useCallback(() => {\n localStorage.setItem(STORAGE_KEYS.appVersion, version);\n setShouldShow(false);\n }, [version]);\n\n return {\n /** Whether the modal should be displayed */\n shouldShow,\n /** Previous app version (before update) */\n previousVersion,\n /** Current app version */\n currentVersion: version,\n /** Mark the modal as shown (call when user dismisses) */\n markAsShown,\n };\n}\n","/**\n * Common Features Hooks\n *\n * React hooks for fetching and using common features in consumer projects.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport { isInitialized } from '../firebase/config';\nimport {\n fetchContactInfo,\n subscribeToContactInfo,\n clearContactInfoCache,\n fetchDeveloperInfo,\n subscribeToDeveloperInfo,\n clearDeveloperInfoCache,\n fetchAddressInfo,\n clearAddressInfoCache,\n fetchSocialLinks,\n clearSocialLinksCache,\n fetchPaymentOptions,\n clearPaymentOptionsCache,\n fetchServices,\n clearServicesCache,\n fetchSkills,\n clearSkillsCache,\n fetchTestimonials,\n clearTestimonialsCache,\n fetchProjects,\n fetchProjectBySlug,\n clearProjectsCache,\n} from '../services/commonFeatures';\nimport type {\n ContactInfo,\n DeveloperInfo,\n AddressInfo,\n SocialLink,\n PaymentOption,\n Service,\n Skill,\n Testimonial,\n Project,\n UseCommonFeatureOptions,\n UseCommonFeatureResult,\n UseCommonFeaturesListResult,\n FetchSocialLinksOptions,\n FetchServicesOptions,\n FetchSkillsOptions,\n FetchTestimonialsOptions,\n FetchPaymentOptionsOptions,\n FetchProjectsOptions,\n} from '../types/commonFeatures';\n\n// ============================================================================\n// CONTACT INFO\n// ============================================================================\n\n/**\n * Hook to fetch contact information\n *\n * @example\n * ```tsx\n * const { data: contact, loading } = useContactInfo();\n *\n * if (loading) return <Spinner />;\n * if (!contact) return null;\n *\n * return <a href={`mailto:${contact.email}`}>{contact.email}</a>;\n * ```\n */\nexport function useContactInfo(\n options: UseCommonFeatureOptions = {}\n): UseCommonFeatureResult<ContactInfo> {\n const { autoFetch = true, realtime = false } = options;\n\n const [data, setData] = useState<ContactInfo | null>(null);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchContactInfo();\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch contact info');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, []);\n\n const refetch = useCallback(async () => {\n clearContactInfoCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n\n if (realtime && isInitialized()) {\n const unsubscribe = subscribeToContactInfo((result) => {\n if (mountedRef.current) {\n setData(result);\n setLoading(false);\n }\n });\n return () => {\n mountedRef.current = false;\n unsubscribe();\n };\n }\n\n if (autoFetch) fetch();\n\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, realtime, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// DEVELOPER INFO\n// ============================================================================\n\n/**\n * Hook to fetch developer information\n *\n * @example\n * ```tsx\n * const { data: developer, loading } = useDeveloperInfo();\n *\n * if (loading) return <Spinner />;\n * if (!developer) return null;\n *\n * return (\n * <div>\n * <h1>{developer.name}</h1>\n * <p>{developer.title}</p>\n * </div>\n * );\n * ```\n */\nexport function useDeveloperInfo(\n options: UseCommonFeatureOptions = {}\n): UseCommonFeatureResult<DeveloperInfo> {\n const { autoFetch = true, realtime = false } = options;\n\n const [data, setData] = useState<DeveloperInfo | null>(null);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchDeveloperInfo();\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch developer info');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, []);\n\n const refetch = useCallback(async () => {\n clearDeveloperInfoCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n\n if (realtime && isInitialized()) {\n const unsubscribe = subscribeToDeveloperInfo((result) => {\n if (mountedRef.current) {\n setData(result);\n setLoading(false);\n }\n });\n return () => {\n mountedRef.current = false;\n unsubscribe();\n };\n }\n\n if (autoFetch) fetch();\n\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, realtime, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// ADDRESS INFO\n// ============================================================================\n\n/**\n * Hook to fetch address information\n */\nexport function useAddressInfo(\n options: UseCommonFeatureOptions = {}\n): UseCommonFeatureResult<AddressInfo> {\n const { autoFetch = true } = options;\n\n const [data, setData] = useState<AddressInfo | null>(null);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchAddressInfo();\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch address info');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, []);\n\n const refetch = useCallback(async () => {\n clearAddressInfoCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// SOCIAL LINKS\n// ============================================================================\n\n/**\n * Hook to fetch social links\n *\n * @example\n * ```tsx\n * const { data: links, loading } = useSocialLinks({ showIn: ['footer'] });\n *\n * if (loading) return <Spinner />;\n *\n * return (\n * <div>\n * {links.map(link => (\n * <a key={link.id} href={link.url}>{link.platform}</a>\n * ))}\n * </div>\n * );\n * ```\n */\nexport function useSocialLinks(\n options: FetchSocialLinksOptions & UseCommonFeatureOptions = {}\n): UseCommonFeaturesListResult<SocialLink> {\n const { autoFetch = true, showIn, activeOnly } = options;\n\n const [data, setData] = useState<SocialLink[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchSocialLinks({ showIn, activeOnly });\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch social links');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [showIn, activeOnly]);\n\n const refetch = useCallback(async () => {\n clearSocialLinksCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// PAYMENT OPTIONS\n// ============================================================================\n\n/**\n * Hook to fetch payment options\n */\nexport function usePaymentOptions(\n options: FetchPaymentOptionsOptions & UseCommonFeatureOptions = {}\n): UseCommonFeaturesListResult<PaymentOption> {\n const { autoFetch = true, activeOnly, type } = options;\n\n const [data, setData] = useState<PaymentOption[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchPaymentOptions({ activeOnly, type });\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch payment options');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [activeOnly, type]);\n\n const refetch = useCallback(async () => {\n clearPaymentOptionsCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// SERVICES\n// ============================================================================\n\n/**\n * Hook to fetch services\n */\nexport function useServices(\n options: FetchServicesOptions & UseCommonFeatureOptions = {}\n): UseCommonFeaturesListResult<Service> {\n const { autoFetch = true, category, activeOnly, featuredOnly } = options;\n\n const [data, setData] = useState<Service[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchServices({ category, activeOnly, featuredOnly });\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch services');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [category, activeOnly, featuredOnly]);\n\n const refetch = useCallback(async () => {\n clearServicesCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// SKILLS\n// ============================================================================\n\n/**\n * Hook to fetch skills\n */\nexport function useSkills(\n options: FetchSkillsOptions & UseCommonFeatureOptions = {}\n): UseCommonFeaturesListResult<Skill> {\n const { autoFetch = true, category, activeOnly, featuredOnly } = options;\n\n const [data, setData] = useState<Skill[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchSkills({ category, activeOnly, featuredOnly });\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch skills');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [category, activeOnly, featuredOnly]);\n\n const refetch = useCallback(async () => {\n clearSkillsCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// TESTIMONIALS\n// ============================================================================\n\n/**\n * Hook to fetch testimonials\n */\nexport function useTestimonials(\n options: FetchTestimonialsOptions & UseCommonFeatureOptions = {}\n): UseCommonFeaturesListResult<Testimonial> {\n const { autoFetch = true, activeOnly, featuredOnly, limit } = options;\n\n const [data, setData] = useState<Testimonial[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchTestimonials({ activeOnly, featuredOnly, limit });\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch testimonials');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [activeOnly, featuredOnly, limit]);\n\n const refetch = useCallback(async () => {\n clearTestimonialsCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n// ============================================================================\n// PROJECTS\n// ============================================================================\n\n/**\n * Hook to fetch projects\n *\n * @example\n * ```tsx\n * const { data: projects, loading } = useProjects({ featuredOnly: true });\n *\n * if (loading) return <Spinner />;\n *\n * return (\n * <div>\n * {projects.map(project => (\n * <div key={project.id}>\n * <h2>{project.title}</h2>\n * <p>{project.description}</p>\n * </div>\n * ))}\n * </div>\n * );\n * ```\n */\nexport function useProjects(\n options: FetchProjectsOptions & UseCommonFeatureOptions = {}\n): UseCommonFeaturesListResult<Project> {\n const { autoFetch = true, category, status, activeOnly, featuredOnly, limit } = options;\n\n const [data, setData] = useState<Project[]>([]);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchProjects({ category, status, activeOnly, featuredOnly, limit });\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch projects');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [category, status, activeOnly, featuredOnly, limit]);\n\n const refetch = useCallback(async () => {\n clearProjectsCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n\n/**\n * Hook to fetch a single project by slug\n */\nexport function useProject(\n slug: string,\n options: { autoFetch?: boolean } = {}\n): UseCommonFeatureResult<Project> {\n const { autoFetch = true } = options;\n\n const [data, setData] = useState<Project | null>(null);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n\n const fetch = useCallback(async () => {\n if (!isInitialized()) {\n setError('shared-features not initialized');\n setLoading(false);\n return;\n }\n\n if (!slug) {\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await fetchProjectBySlug(slug);\n if (mountedRef.current) setData(result);\n } catch (err) {\n if (mountedRef.current) {\n setError(err instanceof Error ? err.message : 'Failed to fetch project');\n }\n } finally {\n if (mountedRef.current) setLoading(false);\n }\n }, [slug]);\n\n const refetch = useCallback(async () => {\n clearProjectsCache();\n await fetch();\n }, [fetch]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (autoFetch) fetch();\n return () => {\n mountedRef.current = false;\n };\n }, [autoFetch, fetch]);\n\n return { data, loading, error, refetch };\n}\n"],"names":["useState","useCallback","isInitialized","fetchActiveCampaigns","isEligibleForCampaign","useEffect","trackImpression","trackClick","trackClose","clearCampaignsCache","useRef","fetchContactInfo","clearContactInfoCache","subscribeToContactInfo","fetchDeveloperInfo","clearDeveloperInfoCache","subscribeToDeveloperInfo","fetchAddressInfo","clearAddressInfoCache","fetchSocialLinks","clearSocialLinksCache","fetchPaymentOptions","clearPaymentOptionsCache","fetchServices","clearServicesCache","fetchSkills","clearSkillsCache","fetchTestimonials","clearTestimonialsCache","fetchProjects","clearProjectsCache","fetchProjectBySlug"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { useState, useCallback, useEffect, useRef } from "react";
2
- import { R as isInitialized, o as fetchActiveCampaigns, P as isEligibleForCampaign, Y as trackImpression, W as trackClick, X as trackClose, d as clearCampaignsCache, p as fetchAddressInfo, a as clearAddressInfoCache, r as fetchContactInfo, e as clearContactInfoCache, T as subscribeToContactInfo, s as fetchDeveloperInfo, f as clearDeveloperInfoCache, U as subscribeToDeveloperInfo, u as fetchPaymentOptions, h as clearPaymentOptionsCache, w as fetchProjectBySlug, j as clearProjectsCache, x as fetchProjects, y as fetchServices, k as clearServicesCache, z as fetchSkills, l as clearSkillsCache, A as fetchSocialLinks, m as clearSocialLinksCache, B as fetchTestimonials, n as clearTestimonialsCache } from "./commonFeatures-BNmLKLa9.js";
2
+ import { R as isInitialized, o as fetchActiveCampaigns, P as isEligibleForCampaign, Y as trackImpression, W as trackClick, X as trackClose, d as clearCampaignsCache, p as fetchAddressInfo, a as clearAddressInfoCache, r as fetchContactInfo, e as clearContactInfoCache, T as subscribeToContactInfo, s as fetchDeveloperInfo, f as clearDeveloperInfoCache, U as subscribeToDeveloperInfo, u as fetchPaymentOptions, h as clearPaymentOptionsCache, w as fetchProjectBySlug, j as clearProjectsCache, x as fetchProjects, y as fetchServices, k as clearServicesCache, z as fetchSkills, l as clearSkillsCache, A as fetchSocialLinks, m as clearSocialLinksCache, B as fetchTestimonials, n as clearTestimonialsCache } from "./commonFeatures-D-MZcecu.js";
3
3
  function useCampaigns(options) {
4
4
  const {
5
5
  placement,
@@ -568,4 +568,4 @@ export {
568
568
  useUpdateAdModal as m,
569
569
  useAddressInfo as u
570
570
  };
571
- //# sourceMappingURL=useCommonFeatures-CZ6IM5Kn.js.map
571
+ //# sourceMappingURL=useCommonFeatures-B2x-8atT.js.map