import { useCallback } from 'react'; import { usePersSDK } from '../providers/PersSDKProvider'; import type { TriggerSourceDTO, TriggerSourceCreateRequestDTO, PaginatedResponseDTO } from '@explorins/pers-sdk'; import type { TriggerSourceQueryOptions } from '@explorins/pers-sdk/trigger-source'; // Re-export for consumers export type { TriggerSourceQueryOptions } from '@explorins/pers-sdk/trigger-source'; /** * React hook for TriggerSource operations in the PERS SDK * * Manages trigger sources which are physical or digital activation points for campaigns: * - **QR_CODE**: Scannable QR codes at physical locations * - **NFC_TAG**: NFC tap points for contactless interactions * - **GPS_GEOFENCE**: Location-based triggers with radius detection * - **API_WEBHOOK**: External system integration triggers * - **TRANSACTION**: Purchase/payment based triggers * * TriggerSources are standalone entities that can be created, managed, and then * assigned to campaigns. This separation allows reuse across multiple campaigns. * * **Admin Only**: All create, update, and delete operations require admin authentication. * * @returns TriggerSource hook with CRUD operations * * @example Basic TriggerSource Operations * ```typescript * function TriggerSourceManager() { * const { * getAll, * getById, * create, * update, * remove * } = useTriggerSources(); * * // List all QR code trigger sources * const loadQRSources = async () => { * const { data: sources } = await getAll({ type: 'QR_CODE' }); * console.log('QR Sources:', sources); * }; * * // Create a new QR code for a store * const createStoreQR = async () => { * const source = await create({ * type: 'QR_CODE', * name: 'Store Entrance QR', * description: 'Scan at the entrance', * businessId: 'business-123', * coordsLatitude: 47.6062, * coordsLongitude: -122.3321 * }); * console.log('Created:', source.id); * }; * } * ``` * * @example GPS Geofence Trigger * ```typescript * const { create } = useTriggerSources(); * * // Create a GPS geofence around a location * const geofence = await create({ * type: 'GPS_GEOFENCE', * name: 'Downtown Area', * coordsLatitude: 47.6062, * coordsLongitude: -122.3321, * metadata: { radiusMeters: 100 } * }); * ``` */ export const useTriggerSources = () => { const { sdk, isInitialized, isAuthenticated } = usePersSDK(); /** * Get trigger sources with optional filters and pagination * * Retrieves trigger sources (QR codes, NFC tags, GPS geofences, webhooks, etc.) * that can be used to activate campaigns. Supports filtering by type, business, * campaign association, and active status. * * @param options - Filter and pagination options * @returns Promise resolving to paginated trigger sources * * @example * ```typescript * // Get all QR code trigger sources * const { data: qrSources } = await getAll({ type: 'QR_CODE' }); * * // Get trigger sources for a specific business * const { data: businessSources, pagination } = await getAll({ * businessId: 'business-123', * active: true, * page: 1, * limit: 20 * }); * * // Get trigger sources assigned to a campaign * const { data: campaignSources } = await getAll({ * campaignId: 'campaign-456' * }); * ``` */ const getAll = useCallback(async ( options?: TriggerSourceQueryOptions ): Promise> => { if (!isInitialized || !sdk) { throw new Error('SDK not initialized. Call initialize() first.'); } try { const result = await sdk.triggerSources.getAll(options); return result; } catch (error) { console.error('Failed to fetch trigger sources:', error); throw error; } }, [sdk, isInitialized]); /** * Get trigger source by ID * * Retrieves detailed information for a specific trigger source including * its type, location, metadata, and configuration. * * @param triggerSourceId - UUID of the trigger source * @returns Promise resolving to trigger source details * @throws {PersApiError} When trigger source with specified ID is not found * * @example * ```typescript * const source = await getById('source-123'); * * console.log('Trigger Source:', source.name); * console.log('Type:', source.type); * console.log('Active:', source.isActive); * * if (source.coordsLatitude && source.coordsLongitude) { * console.log('Location:', source.coordsLatitude, source.coordsLongitude); * } * ``` */ const getById = useCallback(async ( triggerSourceId: string ): Promise => { if (!isInitialized || !sdk) { throw new Error('SDK not initialized. Call initialize() first.'); } try { const result = await sdk.triggerSources.getById(triggerSourceId); return result; } catch (error) { console.error('Failed to fetch trigger source:', error); throw error; } }, [sdk, isInitialized]); /** * Admin: Create a new trigger source * * Creates a new trigger source (QR code, NFC tag, GPS geofence, webhook, or * transaction-based trigger) that can be assigned to campaigns. Requires * administrator privileges. * * @param triggerSource - Trigger source creation data * @returns Promise resolving to created trigger source * @throws {PersApiError} When not authenticated as admin or validation fails * * @example * ```typescript * // Create a QR code trigger source * const qrSource = await create({ * type: 'QR_CODE', * name: 'Store Entrance QR', * description: 'QR code at main entrance', * businessId: 'business-123', * coordsLatitude: 47.6062, * coordsLongitude: -122.3321 * }); * * // Create an NFC tag trigger source * const nfcSource = await create({ * type: 'NFC_TAG', * name: 'Product Display NFC', * metadata: { productId: 'SKU-12345' } * }); * ``` */ const create = useCallback(async ( triggerSource: TriggerSourceCreateRequestDTO ): Promise => { if (!isInitialized || !sdk) { throw new Error('SDK not initialized. Call initialize() first.'); } if (!isAuthenticated) { throw new Error('SDK not authenticated. create requires admin authentication.'); } try { const result = await sdk.triggerSources.create(triggerSource); return result; } catch (error) { console.error('Failed to create trigger source:', error); throw error; } }, [sdk, isInitialized, isAuthenticated]); /** * Admin: Update a trigger source * * Updates an existing trigger source's configuration. All fields are optional; * only provided fields will be updated. Requires administrator privileges. * * @param triggerSourceId - UUID of the trigger source to update * @param triggerSource - Updated trigger source data (partial) * @returns Promise resolving to updated trigger source * @throws {PersApiError} When not authenticated as admin or trigger source not found * * @example * ```typescript * // Update trigger source name and location * const updated = await update('source-123', { * name: 'Updated Store Entrance QR', * description: 'New description', * coordsLatitude: 47.6063, * coordsLongitude: -122.3322 * }); * ``` */ const update = useCallback(async ( triggerSourceId: string, triggerSource: Partial ): Promise => { if (!isInitialized || !sdk) { throw new Error('SDK not initialized. Call initialize() first.'); } if (!isAuthenticated) { throw new Error('SDK not authenticated. update requires admin authentication.'); } try { const result = await sdk.triggerSources.update(triggerSourceId, triggerSource); return result; } catch (error) { console.error('Failed to update trigger source:', error); throw error; } }, [sdk, isInitialized, isAuthenticated]); /** * Admin: Delete a trigger source * * Soft deletes a trigger source, making it inactive. The trigger source will * be removed from any campaigns it was assigned to. Requires administrator * privileges. * * @param triggerSourceId - UUID of the trigger source to delete * @returns Promise resolving to success status * @throws {PersApiError} When not authenticated as admin or trigger source not found * * @example * ```typescript * const success = await remove('source-123'); * console.log('Trigger source deleted:', success); * ``` */ const remove = useCallback(async ( triggerSourceId: string ): Promise => { if (!isInitialized || !sdk) { throw new Error('SDK not initialized. Call initialize() first.'); } if (!isAuthenticated) { throw new Error('SDK not authenticated. remove requires admin authentication.'); } try { const result = await sdk.triggerSources.delete(triggerSourceId); return result; } catch (error) { console.error('Failed to delete trigger source:', error); throw error; } }, [sdk, isInitialized, isAuthenticated]); return { getAll, getById, create, update, remove, isAvailable: isInitialized && !!sdk?.triggerSources, }; }; export type TriggerSourceHook = ReturnType;