shared-features 0.1.1 → 0.1.2

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 (42) hide show
  1. package/dist/admin-commonFeatures-BqSmAHvZ.js +730 -0
  2. package/dist/admin-commonFeatures-BqSmAHvZ.js.map +1 -0
  3. package/dist/{admin-notifications-CLzQc8zF.cjs → admin-commonFeatures-LWADQRoD.cjs} +369 -2
  4. package/dist/admin-commonFeatures-LWADQRoD.cjs.map +1 -0
  5. package/dist/{broadcasts-CK4sGMz4.js → broadcasts-B36bWyZf.js} +2 -2
  6. package/dist/{broadcasts-CK4sGMz4.js.map → broadcasts-B36bWyZf.js.map} +1 -1
  7. package/dist/{broadcasts-6w_9X92Z.cjs → broadcasts-D3_TQybH.cjs} +2 -2
  8. package/dist/{broadcasts-6w_9X92Z.cjs.map → broadcasts-D3_TQybH.cjs.map} +1 -1
  9. package/dist/{commonFeatures-ofZOjnZ0.cjs → commonFeatures-B9NKYWuL.cjs} +86 -1
  10. package/dist/{commonFeatures-ofZOjnZ0.cjs.map → commonFeatures-B9NKYWuL.cjs.map} +1 -1
  11. package/dist/{commonFeatures-CaqcEOik.js → commonFeatures-BNmLKLa9.js} +128 -43
  12. package/dist/{commonFeatures-CaqcEOik.js.map → commonFeatures-BNmLKLa9.js.map} +1 -1
  13. package/dist/components/index.cjs +1 -1
  14. package/dist/components/index.js +1 -1
  15. package/dist/hooks/index.cjs +2 -2
  16. package/dist/hooks/index.js +2 -2
  17. package/dist/{index-BeNmzbTD.js → index-BJXr96cC.js} +3 -3
  18. package/dist/{index-BeNmzbTD.js.map → index-BJXr96cC.js.map} +1 -1
  19. package/dist/{index-DB40ObYe.cjs → index-CvRtEhjW.cjs} +3 -3
  20. package/dist/{index-DB40ObYe.cjs.map → index-CvRtEhjW.cjs.map} +1 -1
  21. package/dist/index.cjs +51 -24
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.js +106 -79
  24. package/dist/services/admin-commonFeatures.d.ts +35 -0
  25. package/dist/services/admin-commonFeatures.d.ts.map +1 -0
  26. package/dist/services/index.cjs +48 -21
  27. package/dist/services/index.cjs.map +1 -1
  28. package/dist/services/index.d.ts +2 -1
  29. package/dist/services/index.d.ts.map +1 -1
  30. package/dist/services/index.js +82 -55
  31. package/dist/{useCommonFeatures-0EU_VRqi.cjs → useCommonFeatures-C4fOj6rp.cjs} +2 -2
  32. package/dist/{useCommonFeatures-0EU_VRqi.cjs.map → useCommonFeatures-C4fOj6rp.cjs.map} +1 -1
  33. package/dist/{useCommonFeatures-DP2YyB7m.js → useCommonFeatures-DgwIAj9a.js} +2 -2
  34. package/dist/{useCommonFeatures-DP2YyB7m.js.map → useCommonFeatures-DgwIAj9a.js.map} +1 -1
  35. package/dist/{useFeatureFlags-DHnOm0sA.js → useFeatureFlags-BHr1EOg0.js} +3 -3
  36. package/dist/{useFeatureFlags-DHnOm0sA.js.map → useFeatureFlags-BHr1EOg0.js.map} +1 -1
  37. package/dist/{useFeatureFlags-DRR1r3rt.cjs → useFeatureFlags-QBLhm28R.cjs} +3 -3
  38. package/dist/{useFeatureFlags-DRR1r3rt.cjs.map → useFeatureFlags-QBLhm28R.cjs.map} +1 -1
  39. package/package.json +1 -1
  40. package/dist/admin-notifications-CLzQc8zF.cjs.map +0 -1
  41. package/dist/admin-notifications-C_7ReIMi.js +0 -363
  42. package/dist/admin-notifications-C_7ReIMi.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"useFeatureFlags-DHnOm0sA.js","sources":["../src/hooks/useBroadcasts.ts","../src/hooks/useFeatureFlags.ts"],"sourcesContent":["/**\n * useBroadcasts Hook\n *\n * React hook for fetching and displaying broadcast notifications in consumer projects.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport {\n fetchActiveBroadcasts,\n subscribeToBroadcasts,\n trackBroadcastImpression,\n trackBroadcastClick,\n trackBroadcastDismiss,\n clearBroadcastsCache,\n} from '../services/broadcasts';\nimport { isInitialized } from '../firebase/config';\nimport type {\n BroadcastNotification,\n BroadcastVariant,\n NotificationPlatform,\n UseBroadcastsReturn,\n} from '../types/notifications';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface UseBroadcastsOptions {\n /** Filter by variant type */\n variant?: BroadcastVariant;\n /** Override platform detection */\n platform?: NotificationPlatform;\n /** Maximum number of broadcasts to return */\n maxBroadcasts?: number;\n /** Whether to auto-fetch on mount (default: true) */\n autoFetch?: boolean;\n /** Whether to subscribe to real-time updates (default: false) */\n realtime?: boolean;\n}\n\n// ============================================================================\n// MAIN HOOK\n// ============================================================================\n\n/**\n * Hook to fetch and manage broadcast notifications\n *\n * @example\n * ```tsx\n * const { broadcasts, isLoading, dismissBroadcast, trackClick } = useBroadcasts({\n * variant: 'banner',\n * maxBroadcasts: 3,\n * });\n *\n * return (\n * <div>\n * {broadcasts.map(broadcast => (\n * <BroadcastBanner\n * key={broadcast.id}\n * broadcast={broadcast}\n * onDismiss={() => dismissBroadcast(broadcast.id)}\n * onActionClick={() => trackClick(broadcast.id)}\n * />\n * ))}\n * </div>\n * );\n * ```\n */\nexport function useBroadcasts(\n options: UseBroadcastsOptions = {}\n): UseBroadcastsReturn {\n const {\n variant,\n platform,\n maxBroadcasts = 10,\n autoFetch = true,\n realtime = false,\n } = options;\n\n const [broadcasts, setBroadcasts] = useState<BroadcastNotification[]>([]);\n const [isLoading, setIsLoading] = useState(autoFetch);\n const [error, setError] = useState<Error | null>(null);\n const dismissedIdsRef = useRef<Set<string>>(new Set());\n const impressionTrackedRef = useRef<Set<string>>(new Set());\n\n // Fetch broadcasts\n const fetchBroadcastsData = useCallback(async () => {\n if (!isInitialized()) {\n setError(new Error('shared-features not initialized'));\n setIsLoading(false);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const allBroadcasts = await fetchActiveBroadcasts({\n variant,\n platform,\n });\n\n // Filter out locally dismissed and limit count\n const filteredBroadcasts = allBroadcasts\n .filter((b) => !dismissedIdsRef.current.has(b.id))\n .slice(0, maxBroadcasts);\n\n setBroadcasts(filteredBroadcasts);\n } catch (err) {\n const errorObj =\n err instanceof Error ? err : new Error('Failed to fetch broadcasts');\n setError(errorObj);\n console.error('[shared-features] Error fetching broadcasts:', err);\n } finally {\n setIsLoading(false);\n }\n }, [variant, platform, maxBroadcasts]);\n\n // Auto-fetch on mount\n useEffect(() => {\n if (autoFetch) {\n fetchBroadcastsData();\n }\n }, [autoFetch, fetchBroadcastsData]);\n\n // Real-time subscription\n useEffect(() => {\n if (!realtime || !isInitialized()) return;\n\n const unsubscribe = subscribeToBroadcasts(\n (newBroadcasts) => {\n // Filter and limit\n const filtered = newBroadcasts\n .filter((b) => !dismissedIdsRef.current.has(b.id))\n .filter((b) => !variant || b.variant === variant)\n .slice(0, maxBroadcasts);\n\n setBroadcasts(filtered);\n setIsLoading(false);\n },\n { variant, platform }\n );\n\n return () => {\n unsubscribe();\n };\n }, [realtime, variant, platform, maxBroadcasts]);\n\n // Dismiss broadcast\n const handleDismissBroadcast = useCallback((broadcastId: string) => {\n // Update local state immediately\n dismissedIdsRef.current.add(broadcastId);\n setBroadcasts((prev) => prev.filter((b) => b.id !== broadcastId));\n\n // Track dismiss and persist\n trackBroadcastDismiss(broadcastId).catch((err) => {\n console.error('[shared-features] Failed to track dismiss:', err);\n });\n }, []);\n\n // Track impression\n const handleTrackImpression = useCallback((broadcastId: string) => {\n // Only track once per session\n if (impressionTrackedRef.current.has(broadcastId)) return;\n impressionTrackedRef.current.add(broadcastId);\n\n trackBroadcastImpression(broadcastId).catch((err) => {\n console.error('[shared-features] Failed to track impression:', err);\n });\n }, []);\n\n // Track click\n const handleTrackClick = useCallback((broadcastId: string) => {\n trackBroadcastClick(broadcastId).catch((err) => {\n console.error('[shared-features] Failed to track click:', err);\n });\n }, []);\n\n // Check if dismissed\n const checkIsDismissed = useCallback((broadcastId: string) => {\n return dismissedIdsRef.current.has(broadcastId);\n }, []);\n\n // Refresh broadcasts\n const handleRefresh = useCallback(async () => {\n clearBroadcastsCache();\n await fetchBroadcastsData();\n }, [fetchBroadcastsData]);\n\n return {\n broadcasts,\n isLoading,\n error,\n dismissBroadcast: handleDismissBroadcast,\n trackImpression: handleTrackImpression,\n trackClick: handleTrackClick,\n isDismissed: checkIsDismissed,\n refresh: handleRefresh,\n };\n}\n\n// ============================================================================\n// SPECIALIZED HOOKS\n// ============================================================================\n\n/**\n * Hook specifically for banner broadcasts\n */\nexport function useBannerBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'banner' });\n}\n\n/**\n * Hook specifically for modal broadcasts\n */\nexport function useModalBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'modal', maxBroadcasts: 1 });\n}\n\n/**\n * Hook specifically for toast broadcasts\n */\nexport function useToastBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'toast' });\n}\n\n/**\n * Hook specifically for bell/notification center broadcasts\n */\nexport function useBellBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'bell' });\n}\n\n// ============================================================================\n// SINGLE BROADCAST HOOK\n// ============================================================================\n\nexport interface UseSingleBroadcastReturn {\n /** The broadcast (first matching one) */\n broadcast: BroadcastNotification | null;\n /** Whether loading */\n isLoading: boolean;\n /** Error if any */\n error: Error | null;\n /** Dismiss the broadcast */\n dismiss: () => void;\n /** Track impression */\n trackImpression: () => void;\n /** Track click */\n trackClick: () => void;\n /** Whether broadcast was dismissed */\n isDismissed: boolean;\n}\n\n/**\n * Hook to get a single broadcast (convenience wrapper)\n *\n * @example\n * ```tsx\n * const { broadcast, dismiss, trackClick } = useSingleBroadcast({ variant: 'modal' });\n *\n * if (!broadcast) return null;\n *\n * return (\n * <AnnouncementModal\n * broadcast={broadcast}\n * onClose={dismiss}\n * onActionClick={trackClick}\n * />\n * );\n * ```\n */\nexport function useSingleBroadcast(\n options: Omit<UseBroadcastsOptions, 'maxBroadcasts'> = {}\n): UseSingleBroadcastReturn {\n const result = useBroadcasts({ ...options, maxBroadcasts: 1 });\n const broadcast = result.broadcasts[0] || null;\n\n return {\n broadcast,\n isLoading: result.isLoading,\n error: result.error,\n dismiss: () => broadcast && result.dismissBroadcast(broadcast.id),\n trackImpression: () => broadcast && result.trackImpression(broadcast.id),\n trackClick: () => broadcast && result.trackClick(broadcast.id),\n isDismissed: broadcast ? result.isDismissed(broadcast.id) : false,\n };\n}\n\n// ============================================================================\n// ANNOUNCEMENT MODAL HOOK\n// ============================================================================\n\nexport interface UseAnnouncementModalReturn {\n /** The modal broadcast to display */\n broadcast: BroadcastNotification | null;\n /** Whether the modal should be shown */\n isOpen: boolean;\n /** Close the modal */\n close: () => void;\n /** Handle action button click */\n handleAction: () => void;\n}\n\n/**\n * Hook for managing announcement modal display\n * Automatically tracks impressions and handles dismissal\n *\n * @example\n * ```tsx\n * const { broadcast, isOpen, close, handleAction } = useAnnouncementModal();\n *\n * return (\n * <Dialog open={isOpen} onOpenChange={(open) => !open && close()}>\n * <DialogContent>\n * <h2>{broadcast?.title}</h2>\n * <p>{broadcast?.message}</p>\n * {broadcast?.actionUrl && (\n * <Button onClick={handleAction}>{broadcast.actionText || 'Learn More'}</Button>\n * )}\n * </DialogContent>\n * </Dialog>\n * );\n * ```\n */\nexport function useAnnouncementModal(): UseAnnouncementModalReturn {\n const { broadcast, dismiss, trackImpression, trackClick } = useSingleBroadcast({\n variant: 'modal',\n });\n\n const [isOpen, setIsOpen] = useState(false);\n const impressionTrackedRef = useRef(false);\n\n // Show modal when broadcast is available\n useEffect(() => {\n if (broadcast && !impressionTrackedRef.current) {\n setIsOpen(true);\n trackImpression();\n impressionTrackedRef.current = true;\n }\n }, [broadcast, trackImpression]);\n\n const handleClose = useCallback(() => {\n setIsOpen(false);\n dismiss();\n }, [dismiss]);\n\n const handleAction = useCallback(() => {\n trackClick();\n if (broadcast?.actionUrl) {\n window.open(broadcast.actionUrl, '_blank');\n }\n handleClose();\n }, [broadcast, trackClick, handleClose]);\n\n return {\n broadcast,\n isOpen,\n close: handleClose,\n handleAction,\n };\n}\n","/**\n * useFeatureFlags Hook\n *\n * React hook for checking feature availability and status in consumer projects.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport {\n getSharedFeaturesStatus,\n checkFeatureAvailability,\n isFeatureEnabled,\n subscribeToFeatureFlags,\n clearFeatureFlagsCache,\n} from '../services/featureFlags';\nimport { isInitialized, getState } from '../firebase/config';\nimport type {\n FeatureId,\n FeatureAvailability,\n SharedFeaturesStatus,\n UseFeatureFlagsOptions,\n UseFeatureFlagsResult,\n ConsumerFeatureVersions,\n} from '../types/featureFlags';\n\n/**\n * Hook to fetch and monitor feature flags\n *\n * @param options - Hook options\n * @returns Feature flags status and utilities\n *\n * @example\n * ```tsx\n * const {\n * status,\n * loading,\n * isFeatureAvailable,\n * hasDeprecatedFeatures\n * } = useFeatureFlags();\n *\n * if (loading) return <Spinner />;\n *\n * if (!status?.operational) {\n * return <MaintenancePage message={status?.maintenanceMessage} />;\n * }\n *\n * if (isFeatureAvailable('contactInfo')) {\n * return <ContactInfo />;\n * }\n * ```\n */\nexport function useFeatureFlags(\n options: UseFeatureFlagsOptions = {}\n): UseFeatureFlagsResult {\n const {\n autoRefresh = false,\n refreshInterval = 5 * 60 * 1000, // 5 minutes\n autoFetch = true,\n } = options;\n\n const [status, setStatus] = useState<SharedFeaturesStatus | null>(null);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n\n // Track mounted state to prevent state updates after unmount\n const mountedRef = useRef(true);\n\n // Get consumer versions from config if available\n const consumerVersions = useRef<ConsumerFeatureVersions | undefined>(\n getState().config?.featureVersions\n );\n\n // Fetch status\n const fetchStatus = 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 newStatus = await getSharedFeaturesStatus(consumerVersions.current);\n\n if (mountedRef.current) {\n setStatus(newStatus);\n }\n } catch (err) {\n const message =\n err instanceof Error ? err.message : 'Failed to fetch feature flags';\n\n if (mountedRef.current) {\n setError(message);\n console.error('[shared-features] Error fetching feature flags:', err);\n }\n } finally {\n if (mountedRef.current) {\n setLoading(false);\n }\n }\n }, []);\n\n // Auto-fetch on mount\n useEffect(() => {\n if (autoFetch) {\n fetchStatus();\n }\n }, [autoFetch, fetchStatus]);\n\n // Auto-refresh interval\n useEffect(() => {\n if (!autoRefresh) return;\n\n const interval = setInterval(() => {\n fetchStatus();\n }, refreshInterval);\n\n return () => clearInterval(interval);\n }, [autoRefresh, refreshInterval, fetchStatus]);\n\n // Cleanup on unmount\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n // Check if a feature is available\n const checkFeatureAvailable = useCallback(\n (featureId: FeatureId): boolean => {\n // Quick check using cached data\n if (!status) return isFeatureEnabled(featureId);\n\n const availability = status.features[featureId];\n return availability?.available ?? false;\n },\n [status]\n );\n\n // Get feature availability details\n const getAvailability = useCallback(\n (featureId: FeatureId): FeatureAvailability | null => {\n if (!status) return null;\n return status.features[featureId] ?? null;\n },\n [status]\n );\n\n // Refetch with cache clear\n const refetch = useCallback(async () => {\n clearFeatureFlagsCache();\n await fetchStatus();\n }, [fetchStatus]);\n\n return {\n status,\n loading,\n error,\n refetch,\n isFeatureAvailable: checkFeatureAvailable,\n getFeatureAvailability: getAvailability,\n hasDeprecatedFeatures: (status?.deprecatedFeatures.length ?? 0) > 0,\n hasUpgradeRequired: (status?.upgradeRequiredFeatures.length ?? 0) > 0,\n };\n}\n\n/**\n * Hook to check a single feature's availability\n *\n * @param featureId - The feature to check\n * @returns Feature availability and loading state\n *\n * @example\n * ```tsx\n * const { available, loading, deprecated } = useFeature('contactInfo');\n *\n * if (loading) return <Spinner />;\n * if (!available) return null;\n *\n * return <ContactInfo />;\n * ```\n */\nexport function useFeature(featureId: FeatureId) {\n const [availability, setAvailability] = useState<FeatureAvailability | null>(\n null\n );\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let mounted = true;\n\n const checkFeature = async () => {\n if (!isInitialized()) {\n setLoading(false);\n return;\n }\n\n try {\n const consumerVersions = getState().config?.featureVersions;\n const result = await checkFeatureAvailability(\n featureId,\n consumerVersions\n );\n\n if (mounted) {\n setAvailability(result);\n }\n } catch (err) {\n console.error(\n `[shared-features] Error checking feature '${featureId}':`,\n err\n );\n } finally {\n if (mounted) {\n setLoading(false);\n }\n }\n };\n\n checkFeature();\n\n return () => {\n mounted = false;\n };\n }, [featureId]);\n\n return {\n /** Whether the feature is available */\n available: availability?.available ?? false,\n /** Whether the check is in progress */\n loading,\n /** Full availability details */\n availability,\n /** Whether the feature is enabled (but might need upgrade) */\n enabled: availability?.enabled ?? false,\n /** Whether using a deprecated version */\n deprecated: availability?.deprecated ?? false,\n /** Whether an upgrade is required */\n upgradeRequired: availability?.upgradeRequired ?? false,\n /** Deprecation warning if applicable */\n deprecationWarning: availability?.deprecationWarning,\n /** Reason feature is unavailable */\n unavailableReason: availability?.unavailableReason,\n };\n}\n\n/**\n * Hook to subscribe to real-time feature flag updates\n *\n * @param callback - Function to call when flags change\n *\n * @example\n * ```tsx\n * useFeatureFlagsSubscription((flags) => {\n * if (flags?.maintenanceMode) {\n * showMaintenanceBanner();\n * }\n * });\n * ```\n */\nexport function useFeatureFlagsSubscription(\n callback: (status: SharedFeaturesStatus | null) => void\n) {\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n useEffect(() => {\n if (!isInitialized()) return;\n\n const consumerVersions = getState().config?.featureVersions;\n\n const unsubscribe = subscribeToFeatureFlags(async (flags) => {\n if (!flags) {\n callbackRef.current(null);\n return;\n }\n\n // Get full status with current flags\n const status = await getSharedFeaturesStatus(consumerVersions);\n callbackRef.current(status);\n });\n\n return () => unsubscribe();\n }, []);\n}\n\n/**\n * Hook to check if shared-features is operational\n *\n * @returns Whether shared-features is operational\n *\n * @example\n * ```tsx\n * const { operational, maintenanceMessage } = useSharedFeaturesOperational();\n *\n * if (!operational) {\n * return <MaintenancePage message={maintenanceMessage} />;\n * }\n * ```\n */\nexport function useSharedFeaturesOperational() {\n const { status, loading } = useFeatureFlags({ autoFetch: true });\n\n return {\n /** Whether shared-features is operational */\n operational: status?.operational ?? false,\n /** Whether check is in progress */\n loading,\n /** Maintenance message if in maintenance mode */\n maintenanceMessage: status?.maintenanceMessage,\n /** Whether in maintenance mode */\n maintenanceMode: status?.maintenanceMode ?? false,\n /** Current API version */\n apiVersion: status?.apiVersion ?? 'v1',\n };\n}\n\n/**\n * Hook for conditional rendering based on feature availability\n *\n * @param featureId - The feature to check\n * @returns Object with show/hide helpers\n *\n * @example\n * ```tsx\n * const { shouldRender, FallbackOrChildren } = useFeatureGate('contactInfo');\n *\n * return (\n * <FallbackOrChildren fallback={<OldContactInfo />}>\n * <NewContactInfo />\n * </FallbackOrChildren>\n * );\n * ```\n */\nexport function useFeatureGate(featureId: FeatureId) {\n const { available, loading, deprecated, deprecationWarning } =\n useFeature(featureId);\n\n // Log deprecation warning in development\n useEffect(() => {\n if (deprecated && deprecationWarning) {\n console.warn(`[shared-features] ${deprecationWarning}`);\n }\n }, [deprecated, deprecationWarning]);\n\n return {\n /** Whether the feature should be rendered */\n shouldRender: available,\n /** Whether still checking availability */\n loading,\n /** Whether using deprecated version */\n deprecated,\n /** Component that renders children if available, fallback otherwise */\n FallbackOrChildren: useCallback(\n ({\n children,\n fallback = null,\n }: {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n }) => {\n if (loading) return null;\n return available ? children : fallback;\n },\n [available, loading]\n ),\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useFeatureFlags-BHr1EOg0.js","sources":["../src/hooks/useBroadcasts.ts","../src/hooks/useFeatureFlags.ts"],"sourcesContent":["/**\n * useBroadcasts Hook\n *\n * React hook for fetching and displaying broadcast notifications in consumer projects.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport {\n fetchActiveBroadcasts,\n subscribeToBroadcasts,\n trackBroadcastImpression,\n trackBroadcastClick,\n trackBroadcastDismiss,\n clearBroadcastsCache,\n} from '../services/broadcasts';\nimport { isInitialized } from '../firebase/config';\nimport type {\n BroadcastNotification,\n BroadcastVariant,\n NotificationPlatform,\n UseBroadcastsReturn,\n} from '../types/notifications';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface UseBroadcastsOptions {\n /** Filter by variant type */\n variant?: BroadcastVariant;\n /** Override platform detection */\n platform?: NotificationPlatform;\n /** Maximum number of broadcasts to return */\n maxBroadcasts?: number;\n /** Whether to auto-fetch on mount (default: true) */\n autoFetch?: boolean;\n /** Whether to subscribe to real-time updates (default: false) */\n realtime?: boolean;\n}\n\n// ============================================================================\n// MAIN HOOK\n// ============================================================================\n\n/**\n * Hook to fetch and manage broadcast notifications\n *\n * @example\n * ```tsx\n * const { broadcasts, isLoading, dismissBroadcast, trackClick } = useBroadcasts({\n * variant: 'banner',\n * maxBroadcasts: 3,\n * });\n *\n * return (\n * <div>\n * {broadcasts.map(broadcast => (\n * <BroadcastBanner\n * key={broadcast.id}\n * broadcast={broadcast}\n * onDismiss={() => dismissBroadcast(broadcast.id)}\n * onActionClick={() => trackClick(broadcast.id)}\n * />\n * ))}\n * </div>\n * );\n * ```\n */\nexport function useBroadcasts(\n options: UseBroadcastsOptions = {}\n): UseBroadcastsReturn {\n const {\n variant,\n platform,\n maxBroadcasts = 10,\n autoFetch = true,\n realtime = false,\n } = options;\n\n const [broadcasts, setBroadcasts] = useState<BroadcastNotification[]>([]);\n const [isLoading, setIsLoading] = useState(autoFetch);\n const [error, setError] = useState<Error | null>(null);\n const dismissedIdsRef = useRef<Set<string>>(new Set());\n const impressionTrackedRef = useRef<Set<string>>(new Set());\n\n // Fetch broadcasts\n const fetchBroadcastsData = useCallback(async () => {\n if (!isInitialized()) {\n setError(new Error('shared-features not initialized'));\n setIsLoading(false);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const allBroadcasts = await fetchActiveBroadcasts({\n variant,\n platform,\n });\n\n // Filter out locally dismissed and limit count\n const filteredBroadcasts = allBroadcasts\n .filter((b) => !dismissedIdsRef.current.has(b.id))\n .slice(0, maxBroadcasts);\n\n setBroadcasts(filteredBroadcasts);\n } catch (err) {\n const errorObj =\n err instanceof Error ? err : new Error('Failed to fetch broadcasts');\n setError(errorObj);\n console.error('[shared-features] Error fetching broadcasts:', err);\n } finally {\n setIsLoading(false);\n }\n }, [variant, platform, maxBroadcasts]);\n\n // Auto-fetch on mount\n useEffect(() => {\n if (autoFetch) {\n fetchBroadcastsData();\n }\n }, [autoFetch, fetchBroadcastsData]);\n\n // Real-time subscription\n useEffect(() => {\n if (!realtime || !isInitialized()) return;\n\n const unsubscribe = subscribeToBroadcasts(\n (newBroadcasts) => {\n // Filter and limit\n const filtered = newBroadcasts\n .filter((b) => !dismissedIdsRef.current.has(b.id))\n .filter((b) => !variant || b.variant === variant)\n .slice(0, maxBroadcasts);\n\n setBroadcasts(filtered);\n setIsLoading(false);\n },\n { variant, platform }\n );\n\n return () => {\n unsubscribe();\n };\n }, [realtime, variant, platform, maxBroadcasts]);\n\n // Dismiss broadcast\n const handleDismissBroadcast = useCallback((broadcastId: string) => {\n // Update local state immediately\n dismissedIdsRef.current.add(broadcastId);\n setBroadcasts((prev) => prev.filter((b) => b.id !== broadcastId));\n\n // Track dismiss and persist\n trackBroadcastDismiss(broadcastId).catch((err) => {\n console.error('[shared-features] Failed to track dismiss:', err);\n });\n }, []);\n\n // Track impression\n const handleTrackImpression = useCallback((broadcastId: string) => {\n // Only track once per session\n if (impressionTrackedRef.current.has(broadcastId)) return;\n impressionTrackedRef.current.add(broadcastId);\n\n trackBroadcastImpression(broadcastId).catch((err) => {\n console.error('[shared-features] Failed to track impression:', err);\n });\n }, []);\n\n // Track click\n const handleTrackClick = useCallback((broadcastId: string) => {\n trackBroadcastClick(broadcastId).catch((err) => {\n console.error('[shared-features] Failed to track click:', err);\n });\n }, []);\n\n // Check if dismissed\n const checkIsDismissed = useCallback((broadcastId: string) => {\n return dismissedIdsRef.current.has(broadcastId);\n }, []);\n\n // Refresh broadcasts\n const handleRefresh = useCallback(async () => {\n clearBroadcastsCache();\n await fetchBroadcastsData();\n }, [fetchBroadcastsData]);\n\n return {\n broadcasts,\n isLoading,\n error,\n dismissBroadcast: handleDismissBroadcast,\n trackImpression: handleTrackImpression,\n trackClick: handleTrackClick,\n isDismissed: checkIsDismissed,\n refresh: handleRefresh,\n };\n}\n\n// ============================================================================\n// SPECIALIZED HOOKS\n// ============================================================================\n\n/**\n * Hook specifically for banner broadcasts\n */\nexport function useBannerBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'banner' });\n}\n\n/**\n * Hook specifically for modal broadcasts\n */\nexport function useModalBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'modal', maxBroadcasts: 1 });\n}\n\n/**\n * Hook specifically for toast broadcasts\n */\nexport function useToastBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'toast' });\n}\n\n/**\n * Hook specifically for bell/notification center broadcasts\n */\nexport function useBellBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'bell' });\n}\n\n// ============================================================================\n// SINGLE BROADCAST HOOK\n// ============================================================================\n\nexport interface UseSingleBroadcastReturn {\n /** The broadcast (first matching one) */\n broadcast: BroadcastNotification | null;\n /** Whether loading */\n isLoading: boolean;\n /** Error if any */\n error: Error | null;\n /** Dismiss the broadcast */\n dismiss: () => void;\n /** Track impression */\n trackImpression: () => void;\n /** Track click */\n trackClick: () => void;\n /** Whether broadcast was dismissed */\n isDismissed: boolean;\n}\n\n/**\n * Hook to get a single broadcast (convenience wrapper)\n *\n * @example\n * ```tsx\n * const { broadcast, dismiss, trackClick } = useSingleBroadcast({ variant: 'modal' });\n *\n * if (!broadcast) return null;\n *\n * return (\n * <AnnouncementModal\n * broadcast={broadcast}\n * onClose={dismiss}\n * onActionClick={trackClick}\n * />\n * );\n * ```\n */\nexport function useSingleBroadcast(\n options: Omit<UseBroadcastsOptions, 'maxBroadcasts'> = {}\n): UseSingleBroadcastReturn {\n const result = useBroadcasts({ ...options, maxBroadcasts: 1 });\n const broadcast = result.broadcasts[0] || null;\n\n return {\n broadcast,\n isLoading: result.isLoading,\n error: result.error,\n dismiss: () => broadcast && result.dismissBroadcast(broadcast.id),\n trackImpression: () => broadcast && result.trackImpression(broadcast.id),\n trackClick: () => broadcast && result.trackClick(broadcast.id),\n isDismissed: broadcast ? result.isDismissed(broadcast.id) : false,\n };\n}\n\n// ============================================================================\n// ANNOUNCEMENT MODAL HOOK\n// ============================================================================\n\nexport interface UseAnnouncementModalReturn {\n /** The modal broadcast to display */\n broadcast: BroadcastNotification | null;\n /** Whether the modal should be shown */\n isOpen: boolean;\n /** Close the modal */\n close: () => void;\n /** Handle action button click */\n handleAction: () => void;\n}\n\n/**\n * Hook for managing announcement modal display\n * Automatically tracks impressions and handles dismissal\n *\n * @example\n * ```tsx\n * const { broadcast, isOpen, close, handleAction } = useAnnouncementModal();\n *\n * return (\n * <Dialog open={isOpen} onOpenChange={(open) => !open && close()}>\n * <DialogContent>\n * <h2>{broadcast?.title}</h2>\n * <p>{broadcast?.message}</p>\n * {broadcast?.actionUrl && (\n * <Button onClick={handleAction}>{broadcast.actionText || 'Learn More'}</Button>\n * )}\n * </DialogContent>\n * </Dialog>\n * );\n * ```\n */\nexport function useAnnouncementModal(): UseAnnouncementModalReturn {\n const { broadcast, dismiss, trackImpression, trackClick } = useSingleBroadcast({\n variant: 'modal',\n });\n\n const [isOpen, setIsOpen] = useState(false);\n const impressionTrackedRef = useRef(false);\n\n // Show modal when broadcast is available\n useEffect(() => {\n if (broadcast && !impressionTrackedRef.current) {\n setIsOpen(true);\n trackImpression();\n impressionTrackedRef.current = true;\n }\n }, [broadcast, trackImpression]);\n\n const handleClose = useCallback(() => {\n setIsOpen(false);\n dismiss();\n }, [dismiss]);\n\n const handleAction = useCallback(() => {\n trackClick();\n if (broadcast?.actionUrl) {\n window.open(broadcast.actionUrl, '_blank');\n }\n handleClose();\n }, [broadcast, trackClick, handleClose]);\n\n return {\n broadcast,\n isOpen,\n close: handleClose,\n handleAction,\n };\n}\n","/**\n * useFeatureFlags Hook\n *\n * React hook for checking feature availability and status in consumer projects.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport {\n getSharedFeaturesStatus,\n checkFeatureAvailability,\n isFeatureEnabled,\n subscribeToFeatureFlags,\n clearFeatureFlagsCache,\n} from '../services/featureFlags';\nimport { isInitialized, getState } from '../firebase/config';\nimport type {\n FeatureId,\n FeatureAvailability,\n SharedFeaturesStatus,\n UseFeatureFlagsOptions,\n UseFeatureFlagsResult,\n ConsumerFeatureVersions,\n} from '../types/featureFlags';\n\n/**\n * Hook to fetch and monitor feature flags\n *\n * @param options - Hook options\n * @returns Feature flags status and utilities\n *\n * @example\n * ```tsx\n * const {\n * status,\n * loading,\n * isFeatureAvailable,\n * hasDeprecatedFeatures\n * } = useFeatureFlags();\n *\n * if (loading) return <Spinner />;\n *\n * if (!status?.operational) {\n * return <MaintenancePage message={status?.maintenanceMessage} />;\n * }\n *\n * if (isFeatureAvailable('contactInfo')) {\n * return <ContactInfo />;\n * }\n * ```\n */\nexport function useFeatureFlags(\n options: UseFeatureFlagsOptions = {}\n): UseFeatureFlagsResult {\n const {\n autoRefresh = false,\n refreshInterval = 5 * 60 * 1000, // 5 minutes\n autoFetch = true,\n } = options;\n\n const [status, setStatus] = useState<SharedFeaturesStatus | null>(null);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n\n // Track mounted state to prevent state updates after unmount\n const mountedRef = useRef(true);\n\n // Get consumer versions from config if available\n const consumerVersions = useRef<ConsumerFeatureVersions | undefined>(\n getState().config?.featureVersions\n );\n\n // Fetch status\n const fetchStatus = 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 newStatus = await getSharedFeaturesStatus(consumerVersions.current);\n\n if (mountedRef.current) {\n setStatus(newStatus);\n }\n } catch (err) {\n const message =\n err instanceof Error ? err.message : 'Failed to fetch feature flags';\n\n if (mountedRef.current) {\n setError(message);\n console.error('[shared-features] Error fetching feature flags:', err);\n }\n } finally {\n if (mountedRef.current) {\n setLoading(false);\n }\n }\n }, []);\n\n // Auto-fetch on mount\n useEffect(() => {\n if (autoFetch) {\n fetchStatus();\n }\n }, [autoFetch, fetchStatus]);\n\n // Auto-refresh interval\n useEffect(() => {\n if (!autoRefresh) return;\n\n const interval = setInterval(() => {\n fetchStatus();\n }, refreshInterval);\n\n return () => clearInterval(interval);\n }, [autoRefresh, refreshInterval, fetchStatus]);\n\n // Cleanup on unmount\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n // Check if a feature is available\n const checkFeatureAvailable = useCallback(\n (featureId: FeatureId): boolean => {\n // Quick check using cached data\n if (!status) return isFeatureEnabled(featureId);\n\n const availability = status.features[featureId];\n return availability?.available ?? false;\n },\n [status]\n );\n\n // Get feature availability details\n const getAvailability = useCallback(\n (featureId: FeatureId): FeatureAvailability | null => {\n if (!status) return null;\n return status.features[featureId] ?? null;\n },\n [status]\n );\n\n // Refetch with cache clear\n const refetch = useCallback(async () => {\n clearFeatureFlagsCache();\n await fetchStatus();\n }, [fetchStatus]);\n\n return {\n status,\n loading,\n error,\n refetch,\n isFeatureAvailable: checkFeatureAvailable,\n getFeatureAvailability: getAvailability,\n hasDeprecatedFeatures: (status?.deprecatedFeatures.length ?? 0) > 0,\n hasUpgradeRequired: (status?.upgradeRequiredFeatures.length ?? 0) > 0,\n };\n}\n\n/**\n * Hook to check a single feature's availability\n *\n * @param featureId - The feature to check\n * @returns Feature availability and loading state\n *\n * @example\n * ```tsx\n * const { available, loading, deprecated } = useFeature('contactInfo');\n *\n * if (loading) return <Spinner />;\n * if (!available) return null;\n *\n * return <ContactInfo />;\n * ```\n */\nexport function useFeature(featureId: FeatureId) {\n const [availability, setAvailability] = useState<FeatureAvailability | null>(\n null\n );\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let mounted = true;\n\n const checkFeature = async () => {\n if (!isInitialized()) {\n setLoading(false);\n return;\n }\n\n try {\n const consumerVersions = getState().config?.featureVersions;\n const result = await checkFeatureAvailability(\n featureId,\n consumerVersions\n );\n\n if (mounted) {\n setAvailability(result);\n }\n } catch (err) {\n console.error(\n `[shared-features] Error checking feature '${featureId}':`,\n err\n );\n } finally {\n if (mounted) {\n setLoading(false);\n }\n }\n };\n\n checkFeature();\n\n return () => {\n mounted = false;\n };\n }, [featureId]);\n\n return {\n /** Whether the feature is available */\n available: availability?.available ?? false,\n /** Whether the check is in progress */\n loading,\n /** Full availability details */\n availability,\n /** Whether the feature is enabled (but might need upgrade) */\n enabled: availability?.enabled ?? false,\n /** Whether using a deprecated version */\n deprecated: availability?.deprecated ?? false,\n /** Whether an upgrade is required */\n upgradeRequired: availability?.upgradeRequired ?? false,\n /** Deprecation warning if applicable */\n deprecationWarning: availability?.deprecationWarning,\n /** Reason feature is unavailable */\n unavailableReason: availability?.unavailableReason,\n };\n}\n\n/**\n * Hook to subscribe to real-time feature flag updates\n *\n * @param callback - Function to call when flags change\n *\n * @example\n * ```tsx\n * useFeatureFlagsSubscription((flags) => {\n * if (flags?.maintenanceMode) {\n * showMaintenanceBanner();\n * }\n * });\n * ```\n */\nexport function useFeatureFlagsSubscription(\n callback: (status: SharedFeaturesStatus | null) => void\n) {\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n useEffect(() => {\n if (!isInitialized()) return;\n\n const consumerVersions = getState().config?.featureVersions;\n\n const unsubscribe = subscribeToFeatureFlags(async (flags) => {\n if (!flags) {\n callbackRef.current(null);\n return;\n }\n\n // Get full status with current flags\n const status = await getSharedFeaturesStatus(consumerVersions);\n callbackRef.current(status);\n });\n\n return () => unsubscribe();\n }, []);\n}\n\n/**\n * Hook to check if shared-features is operational\n *\n * @returns Whether shared-features is operational\n *\n * @example\n * ```tsx\n * const { operational, maintenanceMessage } = useSharedFeaturesOperational();\n *\n * if (!operational) {\n * return <MaintenancePage message={maintenanceMessage} />;\n * }\n * ```\n */\nexport function useSharedFeaturesOperational() {\n const { status, loading } = useFeatureFlags({ autoFetch: true });\n\n return {\n /** Whether shared-features is operational */\n operational: status?.operational ?? false,\n /** Whether check is in progress */\n loading,\n /** Maintenance message if in maintenance mode */\n maintenanceMessage: status?.maintenanceMessage,\n /** Whether in maintenance mode */\n maintenanceMode: status?.maintenanceMode ?? false,\n /** Current API version */\n apiVersion: status?.apiVersion ?? 'v1',\n };\n}\n\n/**\n * Hook for conditional rendering based on feature availability\n *\n * @param featureId - The feature to check\n * @returns Object with show/hide helpers\n *\n * @example\n * ```tsx\n * const { shouldRender, FallbackOrChildren } = useFeatureGate('contactInfo');\n *\n * return (\n * <FallbackOrChildren fallback={<OldContactInfo />}>\n * <NewContactInfo />\n * </FallbackOrChildren>\n * );\n * ```\n */\nexport function useFeatureGate(featureId: FeatureId) {\n const { available, loading, deprecated, deprecationWarning } =\n useFeature(featureId);\n\n // Log deprecation warning in development\n useEffect(() => {\n if (deprecated && deprecationWarning) {\n console.warn(`[shared-features] ${deprecationWarning}`);\n }\n }, [deprecated, deprecationWarning]);\n\n return {\n /** Whether the feature should be rendered */\n shouldRender: available,\n /** Whether still checking availability */\n loading,\n /** Whether using deprecated version */\n deprecated,\n /** Component that renders children if available, fallback otherwise */\n FallbackOrChildren: useCallback(\n ({\n children,\n fallback = null,\n }: {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n }) => {\n if (loading) return null;\n return available ? children : fallback;\n },\n [available, loading]\n ),\n };\n}\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  const react = require("react");
3
- const broadcasts = require("./broadcasts-6w_9X92Z.cjs");
4
- const commonFeatures = require("./commonFeatures-ofZOjnZ0.cjs");
3
+ const broadcasts = require("./broadcasts-D3_TQybH.cjs");
4
+ const commonFeatures = require("./commonFeatures-B9NKYWuL.cjs");
5
5
  function useBroadcasts(options = {}) {
6
6
  const {
7
7
  variant,
@@ -364,4 +364,4 @@ exports.useModalBroadcasts = useModalBroadcasts;
364
364
  exports.useSharedFeaturesOperational = useSharedFeaturesOperational;
365
365
  exports.useSingleBroadcast = useSingleBroadcast;
366
366
  exports.useToastBroadcasts = useToastBroadcasts;
367
- //# sourceMappingURL=useFeatureFlags-DRR1r3rt.cjs.map
367
+ //# sourceMappingURL=useFeatureFlags-QBLhm28R.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useFeatureFlags-DRR1r3rt.cjs","sources":["../src/hooks/useBroadcasts.ts","../src/hooks/useFeatureFlags.ts"],"sourcesContent":["/**\n * useBroadcasts Hook\n *\n * React hook for fetching and displaying broadcast notifications in consumer projects.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport {\n fetchActiveBroadcasts,\n subscribeToBroadcasts,\n trackBroadcastImpression,\n trackBroadcastClick,\n trackBroadcastDismiss,\n clearBroadcastsCache,\n} from '../services/broadcasts';\nimport { isInitialized } from '../firebase/config';\nimport type {\n BroadcastNotification,\n BroadcastVariant,\n NotificationPlatform,\n UseBroadcastsReturn,\n} from '../types/notifications';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface UseBroadcastsOptions {\n /** Filter by variant type */\n variant?: BroadcastVariant;\n /** Override platform detection */\n platform?: NotificationPlatform;\n /** Maximum number of broadcasts to return */\n maxBroadcasts?: number;\n /** Whether to auto-fetch on mount (default: true) */\n autoFetch?: boolean;\n /** Whether to subscribe to real-time updates (default: false) */\n realtime?: boolean;\n}\n\n// ============================================================================\n// MAIN HOOK\n// ============================================================================\n\n/**\n * Hook to fetch and manage broadcast notifications\n *\n * @example\n * ```tsx\n * const { broadcasts, isLoading, dismissBroadcast, trackClick } = useBroadcasts({\n * variant: 'banner',\n * maxBroadcasts: 3,\n * });\n *\n * return (\n * <div>\n * {broadcasts.map(broadcast => (\n * <BroadcastBanner\n * key={broadcast.id}\n * broadcast={broadcast}\n * onDismiss={() => dismissBroadcast(broadcast.id)}\n * onActionClick={() => trackClick(broadcast.id)}\n * />\n * ))}\n * </div>\n * );\n * ```\n */\nexport function useBroadcasts(\n options: UseBroadcastsOptions = {}\n): UseBroadcastsReturn {\n const {\n variant,\n platform,\n maxBroadcasts = 10,\n autoFetch = true,\n realtime = false,\n } = options;\n\n const [broadcasts, setBroadcasts] = useState<BroadcastNotification[]>([]);\n const [isLoading, setIsLoading] = useState(autoFetch);\n const [error, setError] = useState<Error | null>(null);\n const dismissedIdsRef = useRef<Set<string>>(new Set());\n const impressionTrackedRef = useRef<Set<string>>(new Set());\n\n // Fetch broadcasts\n const fetchBroadcastsData = useCallback(async () => {\n if (!isInitialized()) {\n setError(new Error('shared-features not initialized'));\n setIsLoading(false);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const allBroadcasts = await fetchActiveBroadcasts({\n variant,\n platform,\n });\n\n // Filter out locally dismissed and limit count\n const filteredBroadcasts = allBroadcasts\n .filter((b) => !dismissedIdsRef.current.has(b.id))\n .slice(0, maxBroadcasts);\n\n setBroadcasts(filteredBroadcasts);\n } catch (err) {\n const errorObj =\n err instanceof Error ? err : new Error('Failed to fetch broadcasts');\n setError(errorObj);\n console.error('[shared-features] Error fetching broadcasts:', err);\n } finally {\n setIsLoading(false);\n }\n }, [variant, platform, maxBroadcasts]);\n\n // Auto-fetch on mount\n useEffect(() => {\n if (autoFetch) {\n fetchBroadcastsData();\n }\n }, [autoFetch, fetchBroadcastsData]);\n\n // Real-time subscription\n useEffect(() => {\n if (!realtime || !isInitialized()) return;\n\n const unsubscribe = subscribeToBroadcasts(\n (newBroadcasts) => {\n // Filter and limit\n const filtered = newBroadcasts\n .filter((b) => !dismissedIdsRef.current.has(b.id))\n .filter((b) => !variant || b.variant === variant)\n .slice(0, maxBroadcasts);\n\n setBroadcasts(filtered);\n setIsLoading(false);\n },\n { variant, platform }\n );\n\n return () => {\n unsubscribe();\n };\n }, [realtime, variant, platform, maxBroadcasts]);\n\n // Dismiss broadcast\n const handleDismissBroadcast = useCallback((broadcastId: string) => {\n // Update local state immediately\n dismissedIdsRef.current.add(broadcastId);\n setBroadcasts((prev) => prev.filter((b) => b.id !== broadcastId));\n\n // Track dismiss and persist\n trackBroadcastDismiss(broadcastId).catch((err) => {\n console.error('[shared-features] Failed to track dismiss:', err);\n });\n }, []);\n\n // Track impression\n const handleTrackImpression = useCallback((broadcastId: string) => {\n // Only track once per session\n if (impressionTrackedRef.current.has(broadcastId)) return;\n impressionTrackedRef.current.add(broadcastId);\n\n trackBroadcastImpression(broadcastId).catch((err) => {\n console.error('[shared-features] Failed to track impression:', err);\n });\n }, []);\n\n // Track click\n const handleTrackClick = useCallback((broadcastId: string) => {\n trackBroadcastClick(broadcastId).catch((err) => {\n console.error('[shared-features] Failed to track click:', err);\n });\n }, []);\n\n // Check if dismissed\n const checkIsDismissed = useCallback((broadcastId: string) => {\n return dismissedIdsRef.current.has(broadcastId);\n }, []);\n\n // Refresh broadcasts\n const handleRefresh = useCallback(async () => {\n clearBroadcastsCache();\n await fetchBroadcastsData();\n }, [fetchBroadcastsData]);\n\n return {\n broadcasts,\n isLoading,\n error,\n dismissBroadcast: handleDismissBroadcast,\n trackImpression: handleTrackImpression,\n trackClick: handleTrackClick,\n isDismissed: checkIsDismissed,\n refresh: handleRefresh,\n };\n}\n\n// ============================================================================\n// SPECIALIZED HOOKS\n// ============================================================================\n\n/**\n * Hook specifically for banner broadcasts\n */\nexport function useBannerBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'banner' });\n}\n\n/**\n * Hook specifically for modal broadcasts\n */\nexport function useModalBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'modal', maxBroadcasts: 1 });\n}\n\n/**\n * Hook specifically for toast broadcasts\n */\nexport function useToastBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'toast' });\n}\n\n/**\n * Hook specifically for bell/notification center broadcasts\n */\nexport function useBellBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'bell' });\n}\n\n// ============================================================================\n// SINGLE BROADCAST HOOK\n// ============================================================================\n\nexport interface UseSingleBroadcastReturn {\n /** The broadcast (first matching one) */\n broadcast: BroadcastNotification | null;\n /** Whether loading */\n isLoading: boolean;\n /** Error if any */\n error: Error | null;\n /** Dismiss the broadcast */\n dismiss: () => void;\n /** Track impression */\n trackImpression: () => void;\n /** Track click */\n trackClick: () => void;\n /** Whether broadcast was dismissed */\n isDismissed: boolean;\n}\n\n/**\n * Hook to get a single broadcast (convenience wrapper)\n *\n * @example\n * ```tsx\n * const { broadcast, dismiss, trackClick } = useSingleBroadcast({ variant: 'modal' });\n *\n * if (!broadcast) return null;\n *\n * return (\n * <AnnouncementModal\n * broadcast={broadcast}\n * onClose={dismiss}\n * onActionClick={trackClick}\n * />\n * );\n * ```\n */\nexport function useSingleBroadcast(\n options: Omit<UseBroadcastsOptions, 'maxBroadcasts'> = {}\n): UseSingleBroadcastReturn {\n const result = useBroadcasts({ ...options, maxBroadcasts: 1 });\n const broadcast = result.broadcasts[0] || null;\n\n return {\n broadcast,\n isLoading: result.isLoading,\n error: result.error,\n dismiss: () => broadcast && result.dismissBroadcast(broadcast.id),\n trackImpression: () => broadcast && result.trackImpression(broadcast.id),\n trackClick: () => broadcast && result.trackClick(broadcast.id),\n isDismissed: broadcast ? result.isDismissed(broadcast.id) : false,\n };\n}\n\n// ============================================================================\n// ANNOUNCEMENT MODAL HOOK\n// ============================================================================\n\nexport interface UseAnnouncementModalReturn {\n /** The modal broadcast to display */\n broadcast: BroadcastNotification | null;\n /** Whether the modal should be shown */\n isOpen: boolean;\n /** Close the modal */\n close: () => void;\n /** Handle action button click */\n handleAction: () => void;\n}\n\n/**\n * Hook for managing announcement modal display\n * Automatically tracks impressions and handles dismissal\n *\n * @example\n * ```tsx\n * const { broadcast, isOpen, close, handleAction } = useAnnouncementModal();\n *\n * return (\n * <Dialog open={isOpen} onOpenChange={(open) => !open && close()}>\n * <DialogContent>\n * <h2>{broadcast?.title}</h2>\n * <p>{broadcast?.message}</p>\n * {broadcast?.actionUrl && (\n * <Button onClick={handleAction}>{broadcast.actionText || 'Learn More'}</Button>\n * )}\n * </DialogContent>\n * </Dialog>\n * );\n * ```\n */\nexport function useAnnouncementModal(): UseAnnouncementModalReturn {\n const { broadcast, dismiss, trackImpression, trackClick } = useSingleBroadcast({\n variant: 'modal',\n });\n\n const [isOpen, setIsOpen] = useState(false);\n const impressionTrackedRef = useRef(false);\n\n // Show modal when broadcast is available\n useEffect(() => {\n if (broadcast && !impressionTrackedRef.current) {\n setIsOpen(true);\n trackImpression();\n impressionTrackedRef.current = true;\n }\n }, [broadcast, trackImpression]);\n\n const handleClose = useCallback(() => {\n setIsOpen(false);\n dismiss();\n }, [dismiss]);\n\n const handleAction = useCallback(() => {\n trackClick();\n if (broadcast?.actionUrl) {\n window.open(broadcast.actionUrl, '_blank');\n }\n handleClose();\n }, [broadcast, trackClick, handleClose]);\n\n return {\n broadcast,\n isOpen,\n close: handleClose,\n handleAction,\n };\n}\n","/**\n * useFeatureFlags Hook\n *\n * React hook for checking feature availability and status in consumer projects.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport {\n getSharedFeaturesStatus,\n checkFeatureAvailability,\n isFeatureEnabled,\n subscribeToFeatureFlags,\n clearFeatureFlagsCache,\n} from '../services/featureFlags';\nimport { isInitialized, getState } from '../firebase/config';\nimport type {\n FeatureId,\n FeatureAvailability,\n SharedFeaturesStatus,\n UseFeatureFlagsOptions,\n UseFeatureFlagsResult,\n ConsumerFeatureVersions,\n} from '../types/featureFlags';\n\n/**\n * Hook to fetch and monitor feature flags\n *\n * @param options - Hook options\n * @returns Feature flags status and utilities\n *\n * @example\n * ```tsx\n * const {\n * status,\n * loading,\n * isFeatureAvailable,\n * hasDeprecatedFeatures\n * } = useFeatureFlags();\n *\n * if (loading) return <Spinner />;\n *\n * if (!status?.operational) {\n * return <MaintenancePage message={status?.maintenanceMessage} />;\n * }\n *\n * if (isFeatureAvailable('contactInfo')) {\n * return <ContactInfo />;\n * }\n * ```\n */\nexport function useFeatureFlags(\n options: UseFeatureFlagsOptions = {}\n): UseFeatureFlagsResult {\n const {\n autoRefresh = false,\n refreshInterval = 5 * 60 * 1000, // 5 minutes\n autoFetch = true,\n } = options;\n\n const [status, setStatus] = useState<SharedFeaturesStatus | null>(null);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n\n // Track mounted state to prevent state updates after unmount\n const mountedRef = useRef(true);\n\n // Get consumer versions from config if available\n const consumerVersions = useRef<ConsumerFeatureVersions | undefined>(\n getState().config?.featureVersions\n );\n\n // Fetch status\n const fetchStatus = 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 newStatus = await getSharedFeaturesStatus(consumerVersions.current);\n\n if (mountedRef.current) {\n setStatus(newStatus);\n }\n } catch (err) {\n const message =\n err instanceof Error ? err.message : 'Failed to fetch feature flags';\n\n if (mountedRef.current) {\n setError(message);\n console.error('[shared-features] Error fetching feature flags:', err);\n }\n } finally {\n if (mountedRef.current) {\n setLoading(false);\n }\n }\n }, []);\n\n // Auto-fetch on mount\n useEffect(() => {\n if (autoFetch) {\n fetchStatus();\n }\n }, [autoFetch, fetchStatus]);\n\n // Auto-refresh interval\n useEffect(() => {\n if (!autoRefresh) return;\n\n const interval = setInterval(() => {\n fetchStatus();\n }, refreshInterval);\n\n return () => clearInterval(interval);\n }, [autoRefresh, refreshInterval, fetchStatus]);\n\n // Cleanup on unmount\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n // Check if a feature is available\n const checkFeatureAvailable = useCallback(\n (featureId: FeatureId): boolean => {\n // Quick check using cached data\n if (!status) return isFeatureEnabled(featureId);\n\n const availability = status.features[featureId];\n return availability?.available ?? false;\n },\n [status]\n );\n\n // Get feature availability details\n const getAvailability = useCallback(\n (featureId: FeatureId): FeatureAvailability | null => {\n if (!status) return null;\n return status.features[featureId] ?? null;\n },\n [status]\n );\n\n // Refetch with cache clear\n const refetch = useCallback(async () => {\n clearFeatureFlagsCache();\n await fetchStatus();\n }, [fetchStatus]);\n\n return {\n status,\n loading,\n error,\n refetch,\n isFeatureAvailable: checkFeatureAvailable,\n getFeatureAvailability: getAvailability,\n hasDeprecatedFeatures: (status?.deprecatedFeatures.length ?? 0) > 0,\n hasUpgradeRequired: (status?.upgradeRequiredFeatures.length ?? 0) > 0,\n };\n}\n\n/**\n * Hook to check a single feature's availability\n *\n * @param featureId - The feature to check\n * @returns Feature availability and loading state\n *\n * @example\n * ```tsx\n * const { available, loading, deprecated } = useFeature('contactInfo');\n *\n * if (loading) return <Spinner />;\n * if (!available) return null;\n *\n * return <ContactInfo />;\n * ```\n */\nexport function useFeature(featureId: FeatureId) {\n const [availability, setAvailability] = useState<FeatureAvailability | null>(\n null\n );\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let mounted = true;\n\n const checkFeature = async () => {\n if (!isInitialized()) {\n setLoading(false);\n return;\n }\n\n try {\n const consumerVersions = getState().config?.featureVersions;\n const result = await checkFeatureAvailability(\n featureId,\n consumerVersions\n );\n\n if (mounted) {\n setAvailability(result);\n }\n } catch (err) {\n console.error(\n `[shared-features] Error checking feature '${featureId}':`,\n err\n );\n } finally {\n if (mounted) {\n setLoading(false);\n }\n }\n };\n\n checkFeature();\n\n return () => {\n mounted = false;\n };\n }, [featureId]);\n\n return {\n /** Whether the feature is available */\n available: availability?.available ?? false,\n /** Whether the check is in progress */\n loading,\n /** Full availability details */\n availability,\n /** Whether the feature is enabled (but might need upgrade) */\n enabled: availability?.enabled ?? false,\n /** Whether using a deprecated version */\n deprecated: availability?.deprecated ?? false,\n /** Whether an upgrade is required */\n upgradeRequired: availability?.upgradeRequired ?? false,\n /** Deprecation warning if applicable */\n deprecationWarning: availability?.deprecationWarning,\n /** Reason feature is unavailable */\n unavailableReason: availability?.unavailableReason,\n };\n}\n\n/**\n * Hook to subscribe to real-time feature flag updates\n *\n * @param callback - Function to call when flags change\n *\n * @example\n * ```tsx\n * useFeatureFlagsSubscription((flags) => {\n * if (flags?.maintenanceMode) {\n * showMaintenanceBanner();\n * }\n * });\n * ```\n */\nexport function useFeatureFlagsSubscription(\n callback: (status: SharedFeaturesStatus | null) => void\n) {\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n useEffect(() => {\n if (!isInitialized()) return;\n\n const consumerVersions = getState().config?.featureVersions;\n\n const unsubscribe = subscribeToFeatureFlags(async (flags) => {\n if (!flags) {\n callbackRef.current(null);\n return;\n }\n\n // Get full status with current flags\n const status = await getSharedFeaturesStatus(consumerVersions);\n callbackRef.current(status);\n });\n\n return () => unsubscribe();\n }, []);\n}\n\n/**\n * Hook to check if shared-features is operational\n *\n * @returns Whether shared-features is operational\n *\n * @example\n * ```tsx\n * const { operational, maintenanceMessage } = useSharedFeaturesOperational();\n *\n * if (!operational) {\n * return <MaintenancePage message={maintenanceMessage} />;\n * }\n * ```\n */\nexport function useSharedFeaturesOperational() {\n const { status, loading } = useFeatureFlags({ autoFetch: true });\n\n return {\n /** Whether shared-features is operational */\n operational: status?.operational ?? false,\n /** Whether check is in progress */\n loading,\n /** Maintenance message if in maintenance mode */\n maintenanceMessage: status?.maintenanceMessage,\n /** Whether in maintenance mode */\n maintenanceMode: status?.maintenanceMode ?? false,\n /** Current API version */\n apiVersion: status?.apiVersion ?? 'v1',\n };\n}\n\n/**\n * Hook for conditional rendering based on feature availability\n *\n * @param featureId - The feature to check\n * @returns Object with show/hide helpers\n *\n * @example\n * ```tsx\n * const { shouldRender, FallbackOrChildren } = useFeatureGate('contactInfo');\n *\n * return (\n * <FallbackOrChildren fallback={<OldContactInfo />}>\n * <NewContactInfo />\n * </FallbackOrChildren>\n * );\n * ```\n */\nexport function useFeatureGate(featureId: FeatureId) {\n const { available, loading, deprecated, deprecationWarning } =\n useFeature(featureId);\n\n // Log deprecation warning in development\n useEffect(() => {\n if (deprecated && deprecationWarning) {\n console.warn(`[shared-features] ${deprecationWarning}`);\n }\n }, [deprecated, deprecationWarning]);\n\n return {\n /** Whether the feature should be rendered */\n shouldRender: available,\n /** Whether still checking availability */\n loading,\n /** Whether using deprecated version */\n deprecated,\n /** Component that renders children if available, fallback otherwise */\n FallbackOrChildren: useCallback(\n ({\n children,\n fallback = null,\n }: {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n }) => {\n if (loading) return null;\n return available ? children : fallback;\n },\n [available, loading]\n ),\n };\n}\n"],"names":["broadcasts","useState","useRef","useCallback","isInitialized","fetchActiveBroadcasts","useEffect","subscribeToBroadcasts","trackBroadcastDismiss","trackBroadcastImpression","trackBroadcastClick","clearBroadcastsCache","getState","getSharedFeaturesStatus","isFeatureEnabled","clearFeatureFlagsCache","checkFeatureAvailability","subscribeToFeatureFlags"],"mappings":"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"}
1
+ {"version":3,"file":"useFeatureFlags-QBLhm28R.cjs","sources":["../src/hooks/useBroadcasts.ts","../src/hooks/useFeatureFlags.ts"],"sourcesContent":["/**\n * useBroadcasts Hook\n *\n * React hook for fetching and displaying broadcast notifications in consumer projects.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport {\n fetchActiveBroadcasts,\n subscribeToBroadcasts,\n trackBroadcastImpression,\n trackBroadcastClick,\n trackBroadcastDismiss,\n clearBroadcastsCache,\n} from '../services/broadcasts';\nimport { isInitialized } from '../firebase/config';\nimport type {\n BroadcastNotification,\n BroadcastVariant,\n NotificationPlatform,\n UseBroadcastsReturn,\n} from '../types/notifications';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface UseBroadcastsOptions {\n /** Filter by variant type */\n variant?: BroadcastVariant;\n /** Override platform detection */\n platform?: NotificationPlatform;\n /** Maximum number of broadcasts to return */\n maxBroadcasts?: number;\n /** Whether to auto-fetch on mount (default: true) */\n autoFetch?: boolean;\n /** Whether to subscribe to real-time updates (default: false) */\n realtime?: boolean;\n}\n\n// ============================================================================\n// MAIN HOOK\n// ============================================================================\n\n/**\n * Hook to fetch and manage broadcast notifications\n *\n * @example\n * ```tsx\n * const { broadcasts, isLoading, dismissBroadcast, trackClick } = useBroadcasts({\n * variant: 'banner',\n * maxBroadcasts: 3,\n * });\n *\n * return (\n * <div>\n * {broadcasts.map(broadcast => (\n * <BroadcastBanner\n * key={broadcast.id}\n * broadcast={broadcast}\n * onDismiss={() => dismissBroadcast(broadcast.id)}\n * onActionClick={() => trackClick(broadcast.id)}\n * />\n * ))}\n * </div>\n * );\n * ```\n */\nexport function useBroadcasts(\n options: UseBroadcastsOptions = {}\n): UseBroadcastsReturn {\n const {\n variant,\n platform,\n maxBroadcasts = 10,\n autoFetch = true,\n realtime = false,\n } = options;\n\n const [broadcasts, setBroadcasts] = useState<BroadcastNotification[]>([]);\n const [isLoading, setIsLoading] = useState(autoFetch);\n const [error, setError] = useState<Error | null>(null);\n const dismissedIdsRef = useRef<Set<string>>(new Set());\n const impressionTrackedRef = useRef<Set<string>>(new Set());\n\n // Fetch broadcasts\n const fetchBroadcastsData = useCallback(async () => {\n if (!isInitialized()) {\n setError(new Error('shared-features not initialized'));\n setIsLoading(false);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const allBroadcasts = await fetchActiveBroadcasts({\n variant,\n platform,\n });\n\n // Filter out locally dismissed and limit count\n const filteredBroadcasts = allBroadcasts\n .filter((b) => !dismissedIdsRef.current.has(b.id))\n .slice(0, maxBroadcasts);\n\n setBroadcasts(filteredBroadcasts);\n } catch (err) {\n const errorObj =\n err instanceof Error ? err : new Error('Failed to fetch broadcasts');\n setError(errorObj);\n console.error('[shared-features] Error fetching broadcasts:', err);\n } finally {\n setIsLoading(false);\n }\n }, [variant, platform, maxBroadcasts]);\n\n // Auto-fetch on mount\n useEffect(() => {\n if (autoFetch) {\n fetchBroadcastsData();\n }\n }, [autoFetch, fetchBroadcastsData]);\n\n // Real-time subscription\n useEffect(() => {\n if (!realtime || !isInitialized()) return;\n\n const unsubscribe = subscribeToBroadcasts(\n (newBroadcasts) => {\n // Filter and limit\n const filtered = newBroadcasts\n .filter((b) => !dismissedIdsRef.current.has(b.id))\n .filter((b) => !variant || b.variant === variant)\n .slice(0, maxBroadcasts);\n\n setBroadcasts(filtered);\n setIsLoading(false);\n },\n { variant, platform }\n );\n\n return () => {\n unsubscribe();\n };\n }, [realtime, variant, platform, maxBroadcasts]);\n\n // Dismiss broadcast\n const handleDismissBroadcast = useCallback((broadcastId: string) => {\n // Update local state immediately\n dismissedIdsRef.current.add(broadcastId);\n setBroadcasts((prev) => prev.filter((b) => b.id !== broadcastId));\n\n // Track dismiss and persist\n trackBroadcastDismiss(broadcastId).catch((err) => {\n console.error('[shared-features] Failed to track dismiss:', err);\n });\n }, []);\n\n // Track impression\n const handleTrackImpression = useCallback((broadcastId: string) => {\n // Only track once per session\n if (impressionTrackedRef.current.has(broadcastId)) return;\n impressionTrackedRef.current.add(broadcastId);\n\n trackBroadcastImpression(broadcastId).catch((err) => {\n console.error('[shared-features] Failed to track impression:', err);\n });\n }, []);\n\n // Track click\n const handleTrackClick = useCallback((broadcastId: string) => {\n trackBroadcastClick(broadcastId).catch((err) => {\n console.error('[shared-features] Failed to track click:', err);\n });\n }, []);\n\n // Check if dismissed\n const checkIsDismissed = useCallback((broadcastId: string) => {\n return dismissedIdsRef.current.has(broadcastId);\n }, []);\n\n // Refresh broadcasts\n const handleRefresh = useCallback(async () => {\n clearBroadcastsCache();\n await fetchBroadcastsData();\n }, [fetchBroadcastsData]);\n\n return {\n broadcasts,\n isLoading,\n error,\n dismissBroadcast: handleDismissBroadcast,\n trackImpression: handleTrackImpression,\n trackClick: handleTrackClick,\n isDismissed: checkIsDismissed,\n refresh: handleRefresh,\n };\n}\n\n// ============================================================================\n// SPECIALIZED HOOKS\n// ============================================================================\n\n/**\n * Hook specifically for banner broadcasts\n */\nexport function useBannerBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'banner' });\n}\n\n/**\n * Hook specifically for modal broadcasts\n */\nexport function useModalBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'modal', maxBroadcasts: 1 });\n}\n\n/**\n * Hook specifically for toast broadcasts\n */\nexport function useToastBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'toast' });\n}\n\n/**\n * Hook specifically for bell/notification center broadcasts\n */\nexport function useBellBroadcasts(\n options: Omit<UseBroadcastsOptions, 'variant'> = {}\n) {\n return useBroadcasts({ ...options, variant: 'bell' });\n}\n\n// ============================================================================\n// SINGLE BROADCAST HOOK\n// ============================================================================\n\nexport interface UseSingleBroadcastReturn {\n /** The broadcast (first matching one) */\n broadcast: BroadcastNotification | null;\n /** Whether loading */\n isLoading: boolean;\n /** Error if any */\n error: Error | null;\n /** Dismiss the broadcast */\n dismiss: () => void;\n /** Track impression */\n trackImpression: () => void;\n /** Track click */\n trackClick: () => void;\n /** Whether broadcast was dismissed */\n isDismissed: boolean;\n}\n\n/**\n * Hook to get a single broadcast (convenience wrapper)\n *\n * @example\n * ```tsx\n * const { broadcast, dismiss, trackClick } = useSingleBroadcast({ variant: 'modal' });\n *\n * if (!broadcast) return null;\n *\n * return (\n * <AnnouncementModal\n * broadcast={broadcast}\n * onClose={dismiss}\n * onActionClick={trackClick}\n * />\n * );\n * ```\n */\nexport function useSingleBroadcast(\n options: Omit<UseBroadcastsOptions, 'maxBroadcasts'> = {}\n): UseSingleBroadcastReturn {\n const result = useBroadcasts({ ...options, maxBroadcasts: 1 });\n const broadcast = result.broadcasts[0] || null;\n\n return {\n broadcast,\n isLoading: result.isLoading,\n error: result.error,\n dismiss: () => broadcast && result.dismissBroadcast(broadcast.id),\n trackImpression: () => broadcast && result.trackImpression(broadcast.id),\n trackClick: () => broadcast && result.trackClick(broadcast.id),\n isDismissed: broadcast ? result.isDismissed(broadcast.id) : false,\n };\n}\n\n// ============================================================================\n// ANNOUNCEMENT MODAL HOOK\n// ============================================================================\n\nexport interface UseAnnouncementModalReturn {\n /** The modal broadcast to display */\n broadcast: BroadcastNotification | null;\n /** Whether the modal should be shown */\n isOpen: boolean;\n /** Close the modal */\n close: () => void;\n /** Handle action button click */\n handleAction: () => void;\n}\n\n/**\n * Hook for managing announcement modal display\n * Automatically tracks impressions and handles dismissal\n *\n * @example\n * ```tsx\n * const { broadcast, isOpen, close, handleAction } = useAnnouncementModal();\n *\n * return (\n * <Dialog open={isOpen} onOpenChange={(open) => !open && close()}>\n * <DialogContent>\n * <h2>{broadcast?.title}</h2>\n * <p>{broadcast?.message}</p>\n * {broadcast?.actionUrl && (\n * <Button onClick={handleAction}>{broadcast.actionText || 'Learn More'}</Button>\n * )}\n * </DialogContent>\n * </Dialog>\n * );\n * ```\n */\nexport function useAnnouncementModal(): UseAnnouncementModalReturn {\n const { broadcast, dismiss, trackImpression, trackClick } = useSingleBroadcast({\n variant: 'modal',\n });\n\n const [isOpen, setIsOpen] = useState(false);\n const impressionTrackedRef = useRef(false);\n\n // Show modal when broadcast is available\n useEffect(() => {\n if (broadcast && !impressionTrackedRef.current) {\n setIsOpen(true);\n trackImpression();\n impressionTrackedRef.current = true;\n }\n }, [broadcast, trackImpression]);\n\n const handleClose = useCallback(() => {\n setIsOpen(false);\n dismiss();\n }, [dismiss]);\n\n const handleAction = useCallback(() => {\n trackClick();\n if (broadcast?.actionUrl) {\n window.open(broadcast.actionUrl, '_blank');\n }\n handleClose();\n }, [broadcast, trackClick, handleClose]);\n\n return {\n broadcast,\n isOpen,\n close: handleClose,\n handleAction,\n };\n}\n","/**\n * useFeatureFlags Hook\n *\n * React hook for checking feature availability and status in consumer projects.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport {\n getSharedFeaturesStatus,\n checkFeatureAvailability,\n isFeatureEnabled,\n subscribeToFeatureFlags,\n clearFeatureFlagsCache,\n} from '../services/featureFlags';\nimport { isInitialized, getState } from '../firebase/config';\nimport type {\n FeatureId,\n FeatureAvailability,\n SharedFeaturesStatus,\n UseFeatureFlagsOptions,\n UseFeatureFlagsResult,\n ConsumerFeatureVersions,\n} from '../types/featureFlags';\n\n/**\n * Hook to fetch and monitor feature flags\n *\n * @param options - Hook options\n * @returns Feature flags status and utilities\n *\n * @example\n * ```tsx\n * const {\n * status,\n * loading,\n * isFeatureAvailable,\n * hasDeprecatedFeatures\n * } = useFeatureFlags();\n *\n * if (loading) return <Spinner />;\n *\n * if (!status?.operational) {\n * return <MaintenancePage message={status?.maintenanceMessage} />;\n * }\n *\n * if (isFeatureAvailable('contactInfo')) {\n * return <ContactInfo />;\n * }\n * ```\n */\nexport function useFeatureFlags(\n options: UseFeatureFlagsOptions = {}\n): UseFeatureFlagsResult {\n const {\n autoRefresh = false,\n refreshInterval = 5 * 60 * 1000, // 5 minutes\n autoFetch = true,\n } = options;\n\n const [status, setStatus] = useState<SharedFeaturesStatus | null>(null);\n const [loading, setLoading] = useState(autoFetch);\n const [error, setError] = useState<string | null>(null);\n\n // Track mounted state to prevent state updates after unmount\n const mountedRef = useRef(true);\n\n // Get consumer versions from config if available\n const consumerVersions = useRef<ConsumerFeatureVersions | undefined>(\n getState().config?.featureVersions\n );\n\n // Fetch status\n const fetchStatus = 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 newStatus = await getSharedFeaturesStatus(consumerVersions.current);\n\n if (mountedRef.current) {\n setStatus(newStatus);\n }\n } catch (err) {\n const message =\n err instanceof Error ? err.message : 'Failed to fetch feature flags';\n\n if (mountedRef.current) {\n setError(message);\n console.error('[shared-features] Error fetching feature flags:', err);\n }\n } finally {\n if (mountedRef.current) {\n setLoading(false);\n }\n }\n }, []);\n\n // Auto-fetch on mount\n useEffect(() => {\n if (autoFetch) {\n fetchStatus();\n }\n }, [autoFetch, fetchStatus]);\n\n // Auto-refresh interval\n useEffect(() => {\n if (!autoRefresh) return;\n\n const interval = setInterval(() => {\n fetchStatus();\n }, refreshInterval);\n\n return () => clearInterval(interval);\n }, [autoRefresh, refreshInterval, fetchStatus]);\n\n // Cleanup on unmount\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n // Check if a feature is available\n const checkFeatureAvailable = useCallback(\n (featureId: FeatureId): boolean => {\n // Quick check using cached data\n if (!status) return isFeatureEnabled(featureId);\n\n const availability = status.features[featureId];\n return availability?.available ?? false;\n },\n [status]\n );\n\n // Get feature availability details\n const getAvailability = useCallback(\n (featureId: FeatureId): FeatureAvailability | null => {\n if (!status) return null;\n return status.features[featureId] ?? null;\n },\n [status]\n );\n\n // Refetch with cache clear\n const refetch = useCallback(async () => {\n clearFeatureFlagsCache();\n await fetchStatus();\n }, [fetchStatus]);\n\n return {\n status,\n loading,\n error,\n refetch,\n isFeatureAvailable: checkFeatureAvailable,\n getFeatureAvailability: getAvailability,\n hasDeprecatedFeatures: (status?.deprecatedFeatures.length ?? 0) > 0,\n hasUpgradeRequired: (status?.upgradeRequiredFeatures.length ?? 0) > 0,\n };\n}\n\n/**\n * Hook to check a single feature's availability\n *\n * @param featureId - The feature to check\n * @returns Feature availability and loading state\n *\n * @example\n * ```tsx\n * const { available, loading, deprecated } = useFeature('contactInfo');\n *\n * if (loading) return <Spinner />;\n * if (!available) return null;\n *\n * return <ContactInfo />;\n * ```\n */\nexport function useFeature(featureId: FeatureId) {\n const [availability, setAvailability] = useState<FeatureAvailability | null>(\n null\n );\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let mounted = true;\n\n const checkFeature = async () => {\n if (!isInitialized()) {\n setLoading(false);\n return;\n }\n\n try {\n const consumerVersions = getState().config?.featureVersions;\n const result = await checkFeatureAvailability(\n featureId,\n consumerVersions\n );\n\n if (mounted) {\n setAvailability(result);\n }\n } catch (err) {\n console.error(\n `[shared-features] Error checking feature '${featureId}':`,\n err\n );\n } finally {\n if (mounted) {\n setLoading(false);\n }\n }\n };\n\n checkFeature();\n\n return () => {\n mounted = false;\n };\n }, [featureId]);\n\n return {\n /** Whether the feature is available */\n available: availability?.available ?? false,\n /** Whether the check is in progress */\n loading,\n /** Full availability details */\n availability,\n /** Whether the feature is enabled (but might need upgrade) */\n enabled: availability?.enabled ?? false,\n /** Whether using a deprecated version */\n deprecated: availability?.deprecated ?? false,\n /** Whether an upgrade is required */\n upgradeRequired: availability?.upgradeRequired ?? false,\n /** Deprecation warning if applicable */\n deprecationWarning: availability?.deprecationWarning,\n /** Reason feature is unavailable */\n unavailableReason: availability?.unavailableReason,\n };\n}\n\n/**\n * Hook to subscribe to real-time feature flag updates\n *\n * @param callback - Function to call when flags change\n *\n * @example\n * ```tsx\n * useFeatureFlagsSubscription((flags) => {\n * if (flags?.maintenanceMode) {\n * showMaintenanceBanner();\n * }\n * });\n * ```\n */\nexport function useFeatureFlagsSubscription(\n callback: (status: SharedFeaturesStatus | null) => void\n) {\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n useEffect(() => {\n if (!isInitialized()) return;\n\n const consumerVersions = getState().config?.featureVersions;\n\n const unsubscribe = subscribeToFeatureFlags(async (flags) => {\n if (!flags) {\n callbackRef.current(null);\n return;\n }\n\n // Get full status with current flags\n const status = await getSharedFeaturesStatus(consumerVersions);\n callbackRef.current(status);\n });\n\n return () => unsubscribe();\n }, []);\n}\n\n/**\n * Hook to check if shared-features is operational\n *\n * @returns Whether shared-features is operational\n *\n * @example\n * ```tsx\n * const { operational, maintenanceMessage } = useSharedFeaturesOperational();\n *\n * if (!operational) {\n * return <MaintenancePage message={maintenanceMessage} />;\n * }\n * ```\n */\nexport function useSharedFeaturesOperational() {\n const { status, loading } = useFeatureFlags({ autoFetch: true });\n\n return {\n /** Whether shared-features is operational */\n operational: status?.operational ?? false,\n /** Whether check is in progress */\n loading,\n /** Maintenance message if in maintenance mode */\n maintenanceMessage: status?.maintenanceMessage,\n /** Whether in maintenance mode */\n maintenanceMode: status?.maintenanceMode ?? false,\n /** Current API version */\n apiVersion: status?.apiVersion ?? 'v1',\n };\n}\n\n/**\n * Hook for conditional rendering based on feature availability\n *\n * @param featureId - The feature to check\n * @returns Object with show/hide helpers\n *\n * @example\n * ```tsx\n * const { shouldRender, FallbackOrChildren } = useFeatureGate('contactInfo');\n *\n * return (\n * <FallbackOrChildren fallback={<OldContactInfo />}>\n * <NewContactInfo />\n * </FallbackOrChildren>\n * );\n * ```\n */\nexport function useFeatureGate(featureId: FeatureId) {\n const { available, loading, deprecated, deprecationWarning } =\n useFeature(featureId);\n\n // Log deprecation warning in development\n useEffect(() => {\n if (deprecated && deprecationWarning) {\n console.warn(`[shared-features] ${deprecationWarning}`);\n }\n }, [deprecated, deprecationWarning]);\n\n return {\n /** Whether the feature should be rendered */\n shouldRender: available,\n /** Whether still checking availability */\n loading,\n /** Whether using deprecated version */\n deprecated,\n /** Component that renders children if available, fallback otherwise */\n FallbackOrChildren: useCallback(\n ({\n children,\n fallback = null,\n }: {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n }) => {\n if (loading) return null;\n return available ? children : fallback;\n },\n [available, loading]\n ),\n };\n}\n"],"names":["broadcasts","useState","useRef","useCallback","isInitialized","fetchActiveBroadcasts","useEffect","subscribeToBroadcasts","trackBroadcastDismiss","trackBroadcastImpression","trackBroadcastClick","clearBroadcastsCache","getState","getSharedFeaturesStatus","isFeatureEnabled","clearFeatureFlagsCache","checkFeatureAvailability","subscribeToFeatureFlags"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shared-features",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "engines": {
5
5
  "node": ">=24.13.0"
6
6
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"admin-notifications-CLzQc8zF.cjs","sources":["../src/services/admin-notifications.ts"],"sourcesContent":["/**\n * Admin Notifications Service\n *\n * Service for managing broadcasts and templates from the admin panel.\n * Used in aoneahsan.com admin panel to manage cross-project notifications.\n *\n * @author Ahsan Mahmood <aoneahsan@gmail.com>\n */\n\nimport {\n collection,\n doc,\n getDocs,\n getDoc,\n updateDoc,\n deleteDoc,\n addDoc,\n query,\n where,\n orderBy,\n serverTimestamp,\n Timestamp,\n} from 'firebase/firestore';\nimport { getSharedFeaturesDb } from '../firebase/init';\nimport type {\n BroadcastNotification,\n BroadcastStatus,\n CreateBroadcastInput,\n UpdateBroadcastInput,\n NotificationTemplate,\n CreateTemplateInput,\n BroadcastAnalytics,\n NotificationAnalytics,\n} from '../types/notifications';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst COLLECTION_BROADCASTS = 'zaions_broadcasts';\nconst COLLECTION_TEMPLATES = 'zaions_notification_templates';\nconst COLLECTION_BROADCAST_EVENTS = 'zaions_broadcast_events';\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Convert Firestore document to BroadcastNotification\n */\nfunction docToBroadcast(\n docId: string,\n data: Record<string, unknown>\n): BroadcastNotification {\n return {\n id: docId,\n title: data.title as string,\n message: data.message as string,\n type: data.type as BroadcastNotification['type'],\n category: data.category as BroadcastNotification['category'],\n isRead: false,\n isImportant: data.isImportant as boolean | undefined,\n actionUrl: data.actionUrl as string | undefined,\n actionText: data.actionText as string | undefined,\n createdAt: data.createdAt as Timestamp,\n metadata: data.metadata as Record<string, unknown> | undefined,\n targetProjects: (data.targetProjects as string[]) || [],\n targetPlatforms: (data.targetPlatforms as BroadcastNotification['targetPlatforms']) || [],\n targetAudience: (data.targetAudience as BroadcastNotification['targetAudience']) || 'all',\n status: data.status as BroadcastStatus,\n startDate: data.startDate as Timestamp,\n endDate: data.endDate as Timestamp | null | undefined,\n priority: (data.priority as number) || 50,\n dismissible: data.dismissible !== false,\n variant: (data.variant as BroadcastNotification['variant']) || 'banner',\n impressions: (data.impressions as number) || 0,\n clicks: (data.clicks as number) || 0,\n createdBy: data.createdBy as string,\n updatedBy: data.updatedBy as string | undefined,\n };\n}\n\n/**\n * Convert Firestore document to NotificationTemplate\n */\nfunction docToTemplate(\n docId: string,\n data: Record<string, unknown>\n): NotificationTemplate {\n return {\n id: docId,\n name: data.name as string,\n eventType: data.eventType as string,\n category: data.category as NotificationTemplate['category'],\n title: data.title as string,\n message: data.message as string,\n variables: (data.variables as string[]) || [],\n type: data.type as NotificationTemplate['type'],\n isImportant: data.isImportant as boolean,\n actionUrl: data.actionUrl as string | undefined,\n actionText: data.actionText as string | undefined,\n enabled: data.enabled !== false,\n createdAt: (data.createdAt as Timestamp)?.toDate() || new Date(),\n updatedAt: (data.updatedAt as Timestamp)?.toDate() || new Date(),\n };\n}\n\n// ============================================================================\n// BROADCAST CRUD\n// ============================================================================\n\n/**\n * Create a new broadcast\n */\nexport async function createBroadcast(\n input: CreateBroadcastInput,\n adminUserId: string\n): Promise<string> {\n const db = getSharedFeaturesDb();\n\n const broadcastData = {\n ...input,\n status: 'draft' as BroadcastStatus,\n isRead: false,\n impressions: 0,\n clicks: 0,\n createdBy: adminUserId,\n createdAt: serverTimestamp(),\n startDate: Timestamp.fromDate(input.startDate),\n endDate: input.endDate ? Timestamp.fromDate(input.endDate) : null,\n };\n\n const docRef = await addDoc(\n collection(db, COLLECTION_BROADCASTS),\n broadcastData\n );\n\n return docRef.id;\n}\n\n/**\n * Update an existing broadcast\n */\nexport async function updateBroadcast(\n input: UpdateBroadcastInput,\n adminUserId: string\n): Promise<void> {\n const db = getSharedFeaturesDb();\n\n const updateData: Record<string, unknown> = {\n ...input,\n updatedBy: adminUserId,\n updatedAt: serverTimestamp(),\n };\n\n // Convert dates if provided\n if (input.startDate) {\n updateData.startDate = Timestamp.fromDate(input.startDate);\n }\n if (input.endDate !== undefined) {\n updateData.endDate = input.endDate ? Timestamp.fromDate(input.endDate) : null;\n }\n\n // Remove id from update data\n delete updateData.id;\n\n await updateDoc(doc(db, COLLECTION_BROADCASTS, input.id), updateData);\n}\n\n/**\n * Delete a broadcast\n */\nexport async function deleteBroadcast(broadcastId: string): Promise<void> {\n const db = getSharedFeaturesDb();\n await deleteDoc(doc(db, COLLECTION_BROADCASTS, broadcastId));\n}\n\n/**\n * Get all broadcasts (for admin listing)\n */\nexport async function getAllBroadcasts(): Promise<BroadcastNotification[]> {\n const db = getSharedFeaturesDb();\n\n const q = query(\n collection(db, COLLECTION_BROADCASTS),\n orderBy('createdAt', 'desc')\n );\n\n const snapshot = await getDocs(q);\n return snapshot.docs.map((d) => docToBroadcast(d.id, d.data()));\n}\n\n/**\n * Get broadcasts by status\n */\nexport async function getBroadcastsByStatus(\n status: BroadcastStatus\n): Promise<BroadcastNotification[]> {\n const db = getSharedFeaturesDb();\n\n const q = query(\n collection(db, COLLECTION_BROADCASTS),\n where('status', '==', status),\n orderBy('createdAt', 'desc')\n );\n\n const snapshot = await getDocs(q);\n return snapshot.docs.map((d) => docToBroadcast(d.id, d.data()));\n}\n\n/**\n * Get a single broadcast by ID\n */\nexport async function getBroadcastById(\n broadcastId: string\n): Promise<BroadcastNotification | null> {\n const db = getSharedFeaturesDb();\n const docSnap = await getDoc(doc(db, COLLECTION_BROADCASTS, broadcastId));\n\n if (!docSnap.exists()) return null;\n return docToBroadcast(docSnap.id, docSnap.data());\n}\n\n// ============================================================================\n// BROADCAST STATUS MANAGEMENT\n// ============================================================================\n\n/**\n * Publish a draft broadcast (set to active)\n */\nexport async function publishBroadcast(\n broadcastId: string,\n adminUserId: string\n): Promise<void> {\n const db = getSharedFeaturesDb();\n\n await updateDoc(doc(db, COLLECTION_BROADCASTS, broadcastId), {\n status: 'active',\n updatedBy: adminUserId,\n updatedAt: serverTimestamp(),\n });\n}\n\n/**\n * Schedule a broadcast for later\n */\nexport async function scheduleBroadcast(\n broadcastId: string,\n scheduledDate: Date,\n adminUserId: string\n): Promise<void> {\n const db = getSharedFeaturesDb();\n\n await updateDoc(doc(db, COLLECTION_BROADCASTS, broadcastId), {\n status: 'scheduled',\n startDate: Timestamp.fromDate(scheduledDate),\n updatedBy: adminUserId,\n updatedAt: serverTimestamp(),\n });\n}\n\n/**\n * Pause an active broadcast\n */\nexport async function pauseBroadcast(\n broadcastId: string,\n adminUserId: string\n): Promise<void> {\n const db = getSharedFeaturesDb();\n\n await updateDoc(doc(db, COLLECTION_BROADCASTS, broadcastId), {\n status: 'draft', // Move back to draft\n updatedBy: adminUserId,\n updatedAt: serverTimestamp(),\n });\n}\n\n/**\n * End a broadcast\n */\nexport async function endBroadcast(\n broadcastId: string,\n adminUserId: string\n): Promise<void> {\n const db = getSharedFeaturesDb();\n\n await updateDoc(doc(db, COLLECTION_BROADCASTS, broadcastId), {\n status: 'ended',\n endDate: serverTimestamp(),\n updatedBy: adminUserId,\n updatedAt: serverTimestamp(),\n });\n}\n\n// ============================================================================\n// TEMPLATE CRUD\n// ============================================================================\n\n/**\n * Create a new template\n */\nexport async function createTemplate(\n input: CreateTemplateInput\n): Promise<string> {\n const db = getSharedFeaturesDb();\n\n const templateData = {\n ...input,\n createdAt: serverTimestamp(),\n updatedAt: serverTimestamp(),\n };\n\n const docRef = await addDoc(\n collection(db, COLLECTION_TEMPLATES),\n templateData\n );\n\n return docRef.id;\n}\n\n/**\n * Update an existing template\n */\nexport async function updateTemplate(\n templateId: string,\n input: Partial<CreateTemplateInput>\n): Promise<void> {\n const db = getSharedFeaturesDb();\n\n await updateDoc(doc(db, COLLECTION_TEMPLATES, templateId), {\n ...input,\n updatedAt: serverTimestamp(),\n });\n}\n\n/**\n * Delete a template\n */\nexport async function deleteTemplate(templateId: string): Promise<void> {\n const db = getSharedFeaturesDb();\n await deleteDoc(doc(db, COLLECTION_TEMPLATES, templateId));\n}\n\n/**\n * Get all templates\n */\nexport async function getAllTemplates(): Promise<NotificationTemplate[]> {\n const db = getSharedFeaturesDb();\n\n const q = query(\n collection(db, COLLECTION_TEMPLATES),\n orderBy('name', 'asc')\n );\n\n const snapshot = await getDocs(q);\n return snapshot.docs.map((d) => docToTemplate(d.id, d.data()));\n}\n\n/**\n * Get template by ID\n */\nexport async function getTemplateById(\n templateId: string\n): Promise<NotificationTemplate | null> {\n const db = getSharedFeaturesDb();\n const docSnap = await getDoc(doc(db, COLLECTION_TEMPLATES, templateId));\n\n if (!docSnap.exists()) return null;\n return docToTemplate(docSnap.id, docSnap.data());\n}\n\n/**\n * Get template by event type from Firestore\n */\nexport async function getFirestoreTemplateByEventType(\n eventType: string\n): Promise<NotificationTemplate | null> {\n const db = getSharedFeaturesDb();\n\n const q = query(\n collection(db, COLLECTION_TEMPLATES),\n where('eventType', '==', eventType)\n );\n\n const snapshot = await getDocs(q);\n if (snapshot.empty) return null;\n\n const docSnap = snapshot.docs[0];\n if (!docSnap) return null;\n\n return docToTemplate(docSnap.id, docSnap.data());\n}\n\n// ============================================================================\n// ANALYTICS\n// ============================================================================\n\n/**\n * Get analytics for a specific broadcast\n */\nexport async function getBroadcastAnalytics(\n broadcastId: string\n): Promise<BroadcastAnalytics | null> {\n const db = getSharedFeaturesDb();\n\n // Get broadcast\n const broadcast = await getBroadcastById(broadcastId);\n if (!broadcast) return null;\n\n // Get events\n const eventsQuery = query(\n collection(db, COLLECTION_BROADCAST_EVENTS),\n where('broadcastId', '==', broadcastId)\n );\n\n const eventsSnapshot = await getDocs(eventsQuery);\n const events = eventsSnapshot.docs.map((d) => d.data());\n\n // Calculate analytics\n const impressions = events.filter((e) => e.action === 'impression').length;\n const clicks = events.filter((e) => e.action === 'click').length;\n const dismissals = events.filter((e) => e.action === 'dismiss').length;\n\n // Group by platform\n const byPlatform: BroadcastAnalytics['byPlatform'] = {\n web: { impressions: 0, clicks: 0 },\n android: { impressions: 0, clicks: 0 },\n ios: { impressions: 0, clicks: 0 },\n };\n\n events.forEach((e) => {\n const platform = e.platform as keyof typeof byPlatform;\n if (byPlatform[platform]) {\n if (e.action === 'impression') byPlatform[platform].impressions++;\n if (e.action === 'click') byPlatform[platform].clicks++;\n }\n });\n\n // Group by project\n const byProject: BroadcastAnalytics['byProject'] = {};\n events.forEach((e) => {\n const projectId = e.projectId as string;\n if (!byProject[projectId]) {\n byProject[projectId] = { impressions: 0, clicks: 0 };\n }\n if (e.action === 'impression') byProject[projectId].impressions++;\n if (e.action === 'click') byProject[projectId].clicks++;\n });\n\n // Group by date\n const byDateMap: Record<string, { impressions: number; clicks: number }> = {};\n events.forEach((e) => {\n const dateStr = (e.timestamp as Timestamp).toDate().toISOString().split('T')[0];\n if (dateStr) {\n if (!byDateMap[dateStr]) {\n byDateMap[dateStr] = { impressions: 0, clicks: 0 };\n }\n if (e.action === 'impression') byDateMap[dateStr].impressions++;\n if (e.action === 'click') byDateMap[dateStr].clicks++;\n }\n });\n\n const byDate = Object.entries(byDateMap).map(([date, data]) => ({\n date,\n impressions: data.impressions,\n clicks: data.clicks,\n }));\n\n return {\n broadcastId,\n title: broadcast.title,\n status: broadcast.status,\n impressions,\n clicks,\n dismissals,\n ctr: impressions > 0 ? clicks / impressions : 0,\n byPlatform,\n byProject,\n byDate,\n };\n}\n\n/**\n * Get overall notification analytics\n */\nexport async function getOverallAnalytics(\n startDate: Date,\n endDate: Date\n): Promise<NotificationAnalytics> {\n const db = getSharedFeaturesDb();\n\n // Get all broadcast events in date range\n const eventsQuery = query(\n collection(db, COLLECTION_BROADCAST_EVENTS),\n where('timestamp', '>=', Timestamp.fromDate(startDate)),\n where('timestamp', '<=', Timestamp.fromDate(endDate))\n );\n\n const eventsSnapshot = await getDocs(eventsQuery);\n const events = eventsSnapshot.docs.map((d) => d.data());\n\n // Calculate totals\n const totalSent = events.filter((e) => e.action === 'impression').length;\n const totalRead = events.filter((e) => e.action === 'impression').length; // Impressions = read for broadcasts\n const totalClicked = events.filter((e) => e.action === 'click').length;\n\n // Initialize category and type breakdowns\n const byCategory: NotificationAnalytics['byCategory'] = {\n system: { sent: 0, read: 0, clicked: 0 },\n account: { sent: 0, read: 0, clicked: 0 },\n activity: { sent: 0, read: 0, clicked: 0 },\n report: { sent: 0, read: 0, clicked: 0 },\n promotional: { sent: 0, read: 0, clicked: 0 },\n social: { sent: 0, read: 0, clicked: 0 },\n };\n\n const byType: NotificationAnalytics['byType'] = {\n info: { sent: 0, read: 0, clicked: 0 },\n success: { sent: 0, read: 0, clicked: 0 },\n warning: { sent: 0, read: 0, clicked: 0 },\n error: { sent: 0, read: 0, clicked: 0 },\n reminder: { sent: 0, read: 0, clicked: 0 },\n milestone: { sent: 0, read: 0, clicked: 0 },\n announcement: { sent: 0, read: 0, clicked: 0 },\n };\n\n // Group by date\n const byDateMap: Record<string, { sent: number; read: number; clicked: number }> = {};\n events.forEach((e) => {\n const dateStr = (e.timestamp as Timestamp).toDate().toISOString().split('T')[0];\n if (dateStr) {\n if (!byDateMap[dateStr]) {\n byDateMap[dateStr] = { sent: 0, read: 0, clicked: 0 };\n }\n if (e.action === 'impression') {\n byDateMap[dateStr].sent++;\n byDateMap[dateStr].read++;\n }\n if (e.action === 'click') byDateMap[dateStr].clicked++;\n }\n });\n\n const byDate = Object.entries(byDateMap)\n .map(([date, data]) => ({ date, ...data }))\n .sort((a, b) => a.date.localeCompare(b.date));\n\n return {\n totalSent,\n totalRead,\n totalClicked,\n readRate: totalSent > 0 ? totalRead / totalSent : 0,\n clickRate: totalSent > 0 ? totalClicked / totalSent : 0,\n byCategory,\n byType,\n byDate,\n };\n}\n\n// ============================================================================\n// EXPORT SERVICE OBJECT\n// ============================================================================\n\nexport const adminNotificationService = {\n // Broadcasts\n createBroadcast,\n updateBroadcast,\n deleteBroadcast,\n getAllBroadcasts,\n getBroadcastsByStatus,\n getBroadcastById,\n publishBroadcast,\n scheduleBroadcast,\n pauseBroadcast,\n endBroadcast,\n\n // Templates\n createTemplate,\n updateTemplate,\n deleteTemplate,\n getAllTemplates,\n getTemplateById,\n getFirestoreTemplateByEventType,\n\n // Analytics\n getBroadcastAnalytics,\n getOverallAnalytics,\n};\n\nexport default adminNotificationService;\n"],"names":["getSharedFeaturesDb","serverTimestamp","Timestamp","addDoc","collection","updateDoc","doc","deleteDoc","query","orderBy","getDocs","where","getDoc"],"mappings":";;;AAuCA,MAAM,wBAAwB;AAC9B,MAAM,uBAAuB;AAC7B,MAAM,8BAA8B;AASpC,SAAS,eACP,OACA,MACuB;AACvB,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO,KAAK;AAAA,IACZ,SAAS,KAAK;AAAA,IACd,MAAM,KAAK;AAAA,IACX,UAAU,KAAK;AAAA,IACf,QAAQ;AAAA,IACR,aAAa,KAAK;AAAA,IAClB,WAAW,KAAK;AAAA,IAChB,YAAY,KAAK;AAAA,IACjB,WAAW,KAAK;AAAA,IAChB,UAAU,KAAK;AAAA,IACf,gBAAiB,KAAK,kBAA+B,CAAA;AAAA,IACrD,iBAAkB,KAAK,mBAAgE,CAAA;AAAA,IACvF,gBAAiB,KAAK,kBAA8D;AAAA,IACpF,QAAQ,KAAK;AAAA,IACb,WAAW,KAAK;AAAA,IAChB,SAAS,KAAK;AAAA,IACd,UAAW,KAAK,YAAuB;AAAA,IACvC,aAAa,KAAK,gBAAgB;AAAA,IAClC,SAAU,KAAK,WAAgD;AAAA,IAC/D,aAAc,KAAK,eAA0B;AAAA,IAC7C,QAAS,KAAK,UAAqB;AAAA,IACnC,WAAW,KAAK;AAAA,IAChB,WAAW,KAAK;AAAA,EAAA;AAEpB;AAKA,SAAS,cACP,OACA,MACsB;AACtB,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM,KAAK;AAAA,IACX,WAAW,KAAK;AAAA,IAChB,UAAU,KAAK;AAAA,IACf,OAAO,KAAK;AAAA,IACZ,SAAS,KAAK;AAAA,IACd,WAAY,KAAK,aAA0B,CAAA;AAAA,IAC3C,MAAM,KAAK;AAAA,IACX,aAAa,KAAK;AAAA,IAClB,WAAW,KAAK;AAAA,IAChB,YAAY,KAAK;AAAA,IACjB,SAAS,KAAK,YAAY;AAAA,IAC1B,WAAY,KAAK,WAAyB,OAAA,yBAAgB,KAAA;AAAA,IAC1D,WAAY,KAAK,WAAyB,OAAA,yBAAgB,KAAA;AAAA,EAAK;AAEnE;AASA,eAAsB,gBACpB,OACA,aACiB;AACjB,QAAM,KAAKA,eAAAA,oBAAA;AAEX,QAAM,gBAAgB;AAAA,IACpB,GAAG;AAAA,IACH,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,WAAWC,UAAAA,gBAAA;AAAA,IACX,WAAWC,UAAAA,UAAU,SAAS,MAAM,SAAS;AAAA,IAC7C,SAAS,MAAM,UAAUA,UAAAA,UAAU,SAAS,MAAM,OAAO,IAAI;AAAA,EAAA;AAG/D,QAAM,SAAS,MAAMC,UAAAA;AAAAA,IACnBC,UAAAA,WAAW,IAAI,qBAAqB;AAAA,IACpC;AAAA,EAAA;AAGF,SAAO,OAAO;AAChB;AAKA,eAAsB,gBACpB,OACA,aACe;AACf,QAAM,KAAKJ,eAAAA,oBAAA;AAEX,QAAM,aAAsC;AAAA,IAC1C,GAAG;AAAA,IACH,WAAW;AAAA,IACX,WAAWC,UAAAA,gBAAA;AAAA,EAAgB;AAI7B,MAAI,MAAM,WAAW;AACnB,eAAW,YAAYC,UAAAA,UAAU,SAAS,MAAM,SAAS;AAAA,EAC3D;AACA,MAAI,MAAM,YAAY,QAAW;AAC/B,eAAW,UAAU,MAAM,UAAUA,UAAAA,UAAU,SAAS,MAAM,OAAO,IAAI;AAAA,EAC3E;AAGA,SAAO,WAAW;AAElB,QAAMG,UAAAA,UAAUC,UAAAA,IAAI,IAAI,uBAAuB,MAAM,EAAE,GAAG,UAAU;AACtE;AAKA,eAAsB,gBAAgB,aAAoC;AACxE,QAAM,KAAKN,eAAAA,oBAAA;AACX,QAAMO,UAAAA,UAAUD,UAAAA,IAAI,IAAI,uBAAuB,WAAW,CAAC;AAC7D;AAKA,eAAsB,mBAAqD;AACzE,QAAM,KAAKN,eAAAA,oBAAA;AAEX,QAAM,IAAIQ,UAAAA;AAAAA,IACRJ,UAAAA,WAAW,IAAI,qBAAqB;AAAA,IACpCK,UAAAA,QAAQ,aAAa,MAAM;AAAA,EAAA;AAG7B,QAAM,WAAW,MAAMC,UAAAA,QAAQ,CAAC;AAChC,SAAO,SAAS,KAAK,IAAI,CAAC,MAAM,eAAe,EAAE,IAAI,EAAE,KAAA,CAAM,CAAC;AAChE;AAKA,eAAsB,sBACpB,QACkC;AAClC,QAAM,KAAKV,eAAAA,oBAAA;AAEX,QAAM,IAAIQ,UAAAA;AAAAA,IACRJ,UAAAA,WAAW,IAAI,qBAAqB;AAAA,IACpCO,gBAAM,UAAU,MAAM,MAAM;AAAA,IAC5BF,UAAAA,QAAQ,aAAa,MAAM;AAAA,EAAA;AAG7B,QAAM,WAAW,MAAMC,UAAAA,QAAQ,CAAC;AAChC,SAAO,SAAS,KAAK,IAAI,CAAC,MAAM,eAAe,EAAE,IAAI,EAAE,KAAA,CAAM,CAAC;AAChE;AAKA,eAAsB,iBACpB,aACuC;AACvC,QAAM,KAAKV,eAAAA,oBAAA;AACX,QAAM,UAAU,MAAMY,iBAAON,UAAAA,IAAI,IAAI,uBAAuB,WAAW,CAAC;AAExE,MAAI,CAAC,QAAQ,OAAA,EAAU,QAAO;AAC9B,SAAO,eAAe,QAAQ,IAAI,QAAQ,MAAM;AAClD;AASA,eAAsB,iBACpB,aACA,aACe;AACf,QAAM,KAAKN,eAAAA,oBAAA;AAEX,QAAMK,UAAAA,UAAUC,UAAAA,IAAI,IAAI,uBAAuB,WAAW,GAAG;AAAA,IAC3D,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,WAAWL,UAAAA,gBAAA;AAAA,EAAgB,CAC5B;AACH;AAKA,eAAsB,kBACpB,aACA,eACA,aACe;AACf,QAAM,KAAKD,eAAAA,oBAAA;AAEX,QAAMK,UAAAA,UAAUC,UAAAA,IAAI,IAAI,uBAAuB,WAAW,GAAG;AAAA,IAC3D,QAAQ;AAAA,IACR,WAAWJ,UAAAA,UAAU,SAAS,aAAa;AAAA,IAC3C,WAAW;AAAA,IACX,WAAWD,UAAAA,gBAAA;AAAA,EAAgB,CAC5B;AACH;AAKA,eAAsB,eACpB,aACA,aACe;AACf,QAAM,KAAKD,eAAAA,oBAAA;AAEX,QAAMK,UAAAA,UAAUC,UAAAA,IAAI,IAAI,uBAAuB,WAAW,GAAG;AAAA,IAC3D,QAAQ;AAAA;AAAA,IACR,WAAW;AAAA,IACX,WAAWL,UAAAA,gBAAA;AAAA,EAAgB,CAC5B;AACH;AAKA,eAAsB,aACpB,aACA,aACe;AACf,QAAM,KAAKD,eAAAA,oBAAA;AAEX,QAAMK,UAAAA,UAAUC,UAAAA,IAAI,IAAI,uBAAuB,WAAW,GAAG;AAAA,IAC3D,QAAQ;AAAA,IACR,SAASL,UAAAA,gBAAA;AAAA,IACT,WAAW;AAAA,IACX,WAAWA,UAAAA,gBAAA;AAAA,EAAgB,CAC5B;AACH;AASA,eAAsB,eACpB,OACiB;AACjB,QAAM,KAAKD,eAAAA,oBAAA;AAEX,QAAM,eAAe;AAAA,IACnB,GAAG;AAAA,IACH,WAAWC,UAAAA,gBAAA;AAAA,IACX,WAAWA,UAAAA,gBAAA;AAAA,EAAgB;AAG7B,QAAM,SAAS,MAAME,UAAAA;AAAAA,IACnBC,UAAAA,WAAW,IAAI,oBAAoB;AAAA,IACnC;AAAA,EAAA;AAGF,SAAO,OAAO;AAChB;AAKA,eAAsB,eACpB,YACA,OACe;AACf,QAAM,KAAKJ,eAAAA,oBAAA;AAEX,QAAMK,UAAAA,UAAUC,UAAAA,IAAI,IAAI,sBAAsB,UAAU,GAAG;AAAA,IACzD,GAAG;AAAA,IACH,WAAWL,UAAAA,gBAAA;AAAA,EAAgB,CAC5B;AACH;AAKA,eAAsB,eAAe,YAAmC;AACtE,QAAM,KAAKD,eAAAA,oBAAA;AACX,QAAMO,UAAAA,UAAUD,UAAAA,IAAI,IAAI,sBAAsB,UAAU,CAAC;AAC3D;AAKA,eAAsB,kBAAmD;AACvE,QAAM,KAAKN,eAAAA,oBAAA;AAEX,QAAM,IAAIQ,UAAAA;AAAAA,IACRJ,UAAAA,WAAW,IAAI,oBAAoB;AAAA,IACnCK,UAAAA,QAAQ,QAAQ,KAAK;AAAA,EAAA;AAGvB,QAAM,WAAW,MAAMC,UAAAA,QAAQ,CAAC;AAChC,SAAO,SAAS,KAAK,IAAI,CAAC,MAAM,cAAc,EAAE,IAAI,EAAE,KAAA,CAAM,CAAC;AAC/D;AAKA,eAAsB,gBACpB,YACsC;AACtC,QAAM,KAAKV,eAAAA,oBAAA;AACX,QAAM,UAAU,MAAMY,iBAAON,UAAAA,IAAI,IAAI,sBAAsB,UAAU,CAAC;AAEtE,MAAI,CAAC,QAAQ,OAAA,EAAU,QAAO;AAC9B,SAAO,cAAc,QAAQ,IAAI,QAAQ,MAAM;AACjD;AAKA,eAAsB,gCACpB,WACsC;AACtC,QAAM,KAAKN,eAAAA,oBAAA;AAEX,QAAM,IAAIQ,UAAAA;AAAAA,IACRJ,UAAAA,WAAW,IAAI,oBAAoB;AAAA,IACnCO,gBAAM,aAAa,MAAM,SAAS;AAAA,EAAA;AAGpC,QAAM,WAAW,MAAMD,UAAAA,QAAQ,CAAC;AAChC,MAAI,SAAS,MAAO,QAAO;AAE3B,QAAM,UAAU,SAAS,KAAK,CAAC;AAC/B,MAAI,CAAC,QAAS,QAAO;AAErB,SAAO,cAAc,QAAQ,IAAI,QAAQ,MAAM;AACjD;AASA,eAAsB,sBACpB,aACoC;AACpC,QAAM,KAAKV,eAAAA,oBAAA;AAGX,QAAM,YAAY,MAAM,iBAAiB,WAAW;AACpD,MAAI,CAAC,UAAW,QAAO;AAGvB,QAAM,cAAcQ,UAAAA;AAAAA,IAClBJ,UAAAA,WAAW,IAAI,2BAA2B;AAAA,IAC1CO,gBAAM,eAAe,MAAM,WAAW;AAAA,EAAA;AAGxC,QAAM,iBAAiB,MAAMD,UAAAA,QAAQ,WAAW;AAChD,QAAM,SAAS,eAAe,KAAK,IAAI,CAAC,MAAM,EAAE,MAAM;AAGtD,QAAM,cAAc,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,YAAY,EAAE;AACpE,QAAM,SAAS,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,OAAO,EAAE;AAC1D,QAAM,aAAa,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE;AAGhE,QAAM,aAA+C;AAAA,IACnD,KAAK,EAAE,aAAa,GAAG,QAAQ,EAAA;AAAA,IAC/B,SAAS,EAAE,aAAa,GAAG,QAAQ,EAAA;AAAA,IACnC,KAAK,EAAE,aAAa,GAAG,QAAQ,EAAA;AAAA,EAAE;AAGnC,SAAO,QAAQ,CAAC,MAAM;AACpB,UAAM,WAAW,EAAE;AACnB,QAAI,WAAW,QAAQ,GAAG;AACxB,UAAI,EAAE,WAAW,aAAc,YAAW,QAAQ,EAAE;AACpD,UAAI,EAAE,WAAW,QAAS,YAAW,QAAQ,EAAE;AAAA,IACjD;AAAA,EACF,CAAC;AAGD,QAAM,YAA6C,CAAA;AACnD,SAAO,QAAQ,CAAC,MAAM;AACpB,UAAM,YAAY,EAAE;AACpB,QAAI,CAAC,UAAU,SAAS,GAAG;AACzB,gBAAU,SAAS,IAAI,EAAE,aAAa,GAAG,QAAQ,EAAA;AAAA,IACnD;AACA,QAAI,EAAE,WAAW,aAAc,WAAU,SAAS,EAAE;AACpD,QAAI,EAAE,WAAW,QAAS,WAAU,SAAS,EAAE;AAAA,EACjD,CAAC;AAGD,QAAM,YAAqE,CAAA;AAC3E,SAAO,QAAQ,CAAC,MAAM;AACpB,UAAM,UAAW,EAAE,UAAwB,OAAA,EAAS,cAAc,MAAM,GAAG,EAAE,CAAC;AAC9E,QAAI,SAAS;AACX,UAAI,CAAC,UAAU,OAAO,GAAG;AACvB,kBAAU,OAAO,IAAI,EAAE,aAAa,GAAG,QAAQ,EAAA;AAAA,MACjD;AACA,UAAI,EAAE,WAAW,aAAc,WAAU,OAAO,EAAE;AAClD,UAAI,EAAE,WAAW,QAAS,WAAU,OAAO,EAAE;AAAA,IAC/C;AAAA,EACF,CAAC;AAED,QAAM,SAAS,OAAO,QAAQ,SAAS,EAAE,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO;AAAA,IAC9D;AAAA,IACA,aAAa,KAAK;AAAA,IAClB,QAAQ,KAAK;AAAA,EAAA,EACb;AAEF,SAAO;AAAA,IACL;AAAA,IACA,OAAO,UAAU;AAAA,IACjB,QAAQ,UAAU;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK,cAAc,IAAI,SAAS,cAAc;AAAA,IAC9C;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;AAKA,eAAsB,oBACpB,WACA,SACgC;AAChC,QAAM,KAAKV,eAAAA,oBAAA;AAGX,QAAM,cAAcQ,UAAAA;AAAAA,IAClBJ,UAAAA,WAAW,IAAI,2BAA2B;AAAA,IAC1CO,UAAAA,MAAM,aAAa,MAAMT,UAAAA,UAAU,SAAS,SAAS,CAAC;AAAA,IACtDS,UAAAA,MAAM,aAAa,MAAMT,UAAAA,UAAU,SAAS,OAAO,CAAC;AAAA,EAAA;AAGtD,QAAM,iBAAiB,MAAMQ,UAAAA,QAAQ,WAAW;AAChD,QAAM,SAAS,eAAe,KAAK,IAAI,CAAC,MAAM,EAAE,MAAM;AAGtD,QAAM,YAAY,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,YAAY,EAAE;AAClE,QAAM,YAAY,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,YAAY,EAAE;AAClE,QAAM,eAAe,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,OAAO,EAAE;AAGhE,QAAM,aAAkD;AAAA,IACtD,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,IACrC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,IACtC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,IACvC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,IACrC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,IAC1C,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,EAAE;AAGzC,QAAM,SAA0C;AAAA,IAC9C,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,IACnC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,IACtC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,IACtC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,IACpC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,IACvC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,IACxC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,EAAE;AAI/C,QAAM,YAA6E,CAAA;AACnF,SAAO,QAAQ,CAAC,MAAM;AACpB,UAAM,UAAW,EAAE,UAAwB,OAAA,EAAS,cAAc,MAAM,GAAG,EAAE,CAAC;AAC9E,QAAI,SAAS;AACX,UAAI,CAAC,UAAU,OAAO,GAAG;AACvB,kBAAU,OAAO,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAAA;AAAA,MACpD;AACA,UAAI,EAAE,WAAW,cAAc;AAC7B,kBAAU,OAAO,EAAE;AACnB,kBAAU,OAAO,EAAE;AAAA,MACrB;AACA,UAAI,EAAE,WAAW,QAAS,WAAU,OAAO,EAAE;AAAA,IAC/C;AAAA,EACF,CAAC;AAED,QAAM,SAAS,OAAO,QAAQ,SAAS,EACpC,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO,EAAE,MAAM,GAAG,KAAA,EAAO,EACzC,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAE9C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU,YAAY,IAAI,YAAY,YAAY;AAAA,IAClD,WAAW,YAAY,IAAI,eAAe,YAAY;AAAA,IACtD;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;AAMO,MAAM,2BAA2B;AAAA;AAAA,EAEtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AACF;;;;;;;;;;;;;;;;;;;"}