@phygitallabs/tapquest-core 2.0.0

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 (78) hide show
  1. package/README.md +210 -0
  2. package/index.ts +1 -0
  3. package/package.json +43 -0
  4. package/src/constants/firebase.ts +36 -0
  5. package/src/constants/service.ts +30 -0
  6. package/src/helper/helpers.ts +3 -0
  7. package/src/helper/index.ts +1 -0
  8. package/src/index.ts +25 -0
  9. package/src/modules/achievement/helpers/index.ts +98 -0
  10. package/src/modules/achievement/hooks/index.ts +171 -0
  11. package/src/modules/achievement/index.ts +5 -0
  12. package/src/modules/achievement/types/index.ts +45 -0
  13. package/src/modules/achivementWithReward/hooks/achivementPlusRewardModel.ts +83 -0
  14. package/src/modules/achivementWithReward/hooks/index.ts +5 -0
  15. package/src/modules/achivementWithReward/index.ts +5 -0
  16. package/src/modules/auth/README.md +527 -0
  17. package/src/modules/auth/constants/index.ts +9 -0
  18. package/src/modules/auth/helpers/index.ts +161 -0
  19. package/src/modules/auth/helpers/refreshToken.ts +63 -0
  20. package/src/modules/auth/index.ts +20 -0
  21. package/src/modules/auth/providers/AuthProvider.tsx +207 -0
  22. package/src/modules/auth/providers/index.ts +1 -0
  23. package/src/modules/auth/services/FirebaseAuthService.ts +290 -0
  24. package/src/modules/auth/services/authServiceFactory.ts +22 -0
  25. package/src/modules/auth/services/index.ts +3 -0
  26. package/src/modules/auth/store/authSlice.ts +137 -0
  27. package/src/modules/auth/types/index.ts +109 -0
  28. package/src/modules/campaign/hooks/index.ts +6 -0
  29. package/src/modules/campaign/hooks/useCampaignService.ts +7 -0
  30. package/src/modules/campaign/index.tsx +7 -0
  31. package/src/modules/campaign/types/campaign.ts +51 -0
  32. package/src/modules/campaign/types/enums.ts +4 -0
  33. package/src/modules/campaign/types/index.ts +4 -0
  34. package/src/modules/campaign/types/requests.ts +46 -0
  35. package/src/modules/data-tracking/hooks/index.ts +67 -0
  36. package/src/modules/data-tracking/index.ts +1 -0
  37. package/src/modules/generate-certificate/hooks/index.ts +8 -0
  38. package/src/modules/generate-certificate/index.ts +3 -0
  39. package/src/modules/generate-certificate/types/generateCertificate.ts +7 -0
  40. package/src/modules/generate-certificate/types/index.ts +7 -0
  41. package/src/modules/location/hooks/index.ts +8 -0
  42. package/src/modules/location/hooks/useLocationService.ts +8 -0
  43. package/src/modules/location/index.tsx +11 -0
  44. package/src/modules/location/types/index.ts +18 -0
  45. package/src/modules/location/types/locationModel.ts +21 -0
  46. package/src/modules/location/utils/index.ts +5 -0
  47. package/src/modules/location/utils/locationHelpers.ts +13 -0
  48. package/src/modules/memory/hooks/index.ts +3 -0
  49. package/src/modules/memory/index.ts +3 -0
  50. package/src/modules/memory/types/index.ts +3 -0
  51. package/src/modules/notification/index.ts +2 -0
  52. package/src/modules/notification/providers/index.tsx +50 -0
  53. package/src/modules/notification/types/index.ts +3 -0
  54. package/src/modules/reward/hooks/index.ts +14 -0
  55. package/src/modules/reward/hooks/useRewardService.ts +14 -0
  56. package/src/modules/reward/index.tsx +16 -0
  57. package/src/modules/reward/types/enums.ts +13 -0
  58. package/src/modules/reward/types/index.ts +4 -0
  59. package/src/modules/reward/types/requests.ts +281 -0
  60. package/src/modules/reward/types/reward.ts +90 -0
  61. package/src/modules/scan-chip/hooks/index.tsx +67 -0
  62. package/src/modules/scan-chip/index.ts +2 -0
  63. package/src/modules/scan-chip/types/index.ts +25 -0
  64. package/src/modules/send-email/hooks/index.ts +2 -0
  65. package/src/modules/send-email/index.ts +1 -0
  66. package/src/modules/user-profile/hooks/index.ts +3 -0
  67. package/src/modules/user-profile/index.ts +3 -0
  68. package/src/modules/user-profile/types/index.ts +3 -0
  69. package/src/providers/ServicesProvider.tsx +173 -0
  70. package/src/providers/TapquestCoreProvider.tsx +64 -0
  71. package/src/providers/index.ts +1 -0
  72. package/src/store/hooks.ts +6 -0
  73. package/src/store/index.ts +45 -0
  74. package/src/types/common.d.ts +8 -0
  75. package/src/types/media.ts +26 -0
  76. package/src/types/service.d.ts +34 -0
  77. package/tsconfig.json +28 -0
  78. package/tsup.config.ts +10 -0
package/README.md ADDED
@@ -0,0 +1,210 @@
1
+ # @phygitallabs/tapquest-core
2
+
3
+ A comprehensive React SDK that provides unified access to all Tapquest platform services including authentication, achievements, rewards, notifications, and certificate generation.
4
+
5
+ ## Overview
6
+
7
+ Tapquest Core is the central SDK for building applications on the Phygital Labs Tapquest platform. It consolidates multiple specialized services into a single, easy-to-use React provider that handles authentication, service configuration, and API communication across different environments.
8
+
9
+ ## Features
10
+
11
+ - **Unified Provider Architecture**: Single `TapquestCoreProvider` that wraps all platform services
12
+ - **Multi-Environment Support**: Seamless switching between DEV, STAGING, and PRODUCTION environments
13
+ - **Service Modules**:
14
+ - Authentication & Firebase integration (v1.0)
15
+ - Scan chip
16
+ - Location
17
+ - Campaign
18
+ - Achievement
19
+ - Reward
20
+ - Notifications
21
+ - Generate Certificate
22
+ - Ads
23
+ - Data tracking
24
+ - **Built-in State Management**: Uses Zustand for efficient state management
25
+ - **React Query Integration**: Optimized API caching and synchronization
26
+ - **TypeScript Support**: Full type safety with generated type definitions
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ # Using pnpm (recommended for monorepo)
32
+ pnpm add @phygitallabs/tapquest-core
33
+
34
+ # Using npm
35
+ npm install @phygitallabs/tapquest-core
36
+
37
+ # Using yarn
38
+ yarn add @phygitallabs/tapquest-core
39
+ ```
40
+
41
+ ## Quick Start
42
+
43
+ ### 1. Basic Setup
44
+
45
+ ```tsx
46
+ import React from 'react';
47
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
48
+ import { TapquestCoreProvider } from '@phygitallabs/tapquest-core';
49
+
50
+ // Create a query client instance
51
+ const queryClient = new QueryClient();
52
+
53
+ // Define your API configuration
54
+ const apiConfig = {
55
+ environment: 'DEV', // 'DEV' | 'STAGING' | 'PRODUCTION'
56
+ version: 'v1' // 'v1' | 'v2'
57
+ };
58
+
59
+ function App() {
60
+ return (
61
+ <QueryClientProvider client={queryClient}>
62
+ <TapquestCoreProvider
63
+ queryClient={queryClient}
64
+ apiConfig={apiConfig}
65
+ >
66
+ {/* Your app components */}
67
+ <YourAppContent />
68
+ </TapquestCoreProvider>
69
+ </QueryClientProvider>
70
+ );
71
+ }
72
+ ```
73
+
74
+ ### 2. Using Services
75
+
76
+ Once wrapped with `TapquestCoreProvider`, all Tapquest services are available throughout your application:
77
+
78
+ ```tsx
79
+ import {
80
+ // Achievement services
81
+ useAchievements,
82
+
83
+ // Reward services
84
+ useRewards,
85
+
86
+ // Notification services
87
+ useNotifications,
88
+
89
+ // Certificate services
90
+ useCertificateGeneration,
91
+
92
+ // Auth services (when implemented)
93
+ // useAuth
94
+ } from '@phygitallabs/tapquest-core';
95
+
96
+ function YourComponent() {
97
+ const { achievements, loading } = useAchievements();
98
+ const { rewards } = useRewards();
99
+
100
+ return (
101
+ <div>
102
+ {/* Render your content using the services */}
103
+ </div>
104
+ );
105
+ }
106
+ ```
107
+
108
+ ## Configuration
109
+
110
+ ### API Configuration
111
+
112
+ The `apiConfig` object configures which environment and API version to use:
113
+
114
+ ```tsx
115
+ interface APIConfig {
116
+ environment: 'DEV' | 'STAGING' | 'PRODUCTION';
117
+ version: 'v1' | 'v2';
118
+ }
119
+ ```
120
+
121
+ ### Environment URLs
122
+
123
+ The SDK automatically routes requests to the appropriate endpoints based on your environment:
124
+
125
+ - **Development**: `https://api.dev.phygital.vn`
126
+ - **Staging**: `https://api.staging.phygital.vn`
127
+ - **Production**: `https://api.phygital.vn`
128
+
129
+ ## Architecture
130
+
131
+ ### Provider Hierarchy
132
+
133
+ ```
134
+ TapquestCoreProvider
135
+ ├── FirebaseProvider (Authentication & Real-time features)
136
+ ├── AuthProvider (User authentication state)
137
+ └── ServicesProvider (API services & configuration)
138
+ ├── Achievement Service
139
+ ├── Reward Service
140
+ ├── Notification Service
141
+ └── Certificate Service
142
+ ```
143
+
144
+ ### Module Structure
145
+
146
+ ```
147
+ src/
148
+ ├── constants/ # Environment and service configurations
149
+ ├── modules/ # Service-specific modules
150
+ │ ├── achievement/ # Achievement system integration
151
+ │ ├── auth/ # Authentication utilities (in development)
152
+ │ ├── notification/ # Push notification services
153
+ │ ├── reward/ # Reward management
154
+ │ └── generate-certificate/ # Certificate generation
155
+ ├── providers/ # React providers and context
156
+ └── types/ # TypeScript type definitions
157
+ ```
158
+
159
+ ## Development
160
+
161
+ ### Building
162
+
163
+ ```bash
164
+ # Build the package
165
+ pnpm build
166
+
167
+ # Build in watch mode for development
168
+ pnpm dev
169
+ ```
170
+
171
+ ### Dependencies
172
+
173
+ **Core Dependencies:**
174
+ - React 18.2+
175
+ - @tanstack/react-query ^5.66.8
176
+ - Zustand ^5.0.8
177
+ - Axios ^1.8.4
178
+
179
+ **Internal Workspace Dependencies:**
180
+ - @phygitallabs/achievement
181
+ - @phygitallabs/api-core
182
+ - @phygitallabs/generate-certificate
183
+ - @phygitallabs/notification-api
184
+ - @phygitallabs/reward
185
+ - pgl-platform
186
+
187
+ ## TypeScript Support
188
+
189
+ This package is built with TypeScript and includes full type definitions. All exported functions, hooks, and components are fully typed for the best development experience.
190
+
191
+ ```tsx
192
+ import type { APIConfig, ServiceConfig } from '@phygitallabs/tapquest-core';
193
+ ```
194
+
195
+ ## Contributing
196
+
197
+ This package is part of the Phygital Labs monorepo. When contributing:
198
+
199
+ 1. Ensure all new features include TypeScript types
200
+ 2. Follow the existing module structure for new services
201
+ 3. Update this README when adding new functionality
202
+ 4. Run tests and build before submitting changes
203
+
204
+ ## License
205
+
206
+ Private package - All rights reserved by Phygital Labs.
207
+
208
+ ## Support
209
+
210
+ For questions or issues, please contact the Phygital Labs development team or create an issue in the project repository.
package/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src";
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@phygitallabs/tapquest-core",
3
+ "version": "2.0.0",
4
+ "private": false,
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "type": "module",
9
+ "scripts": {
10
+ "preversion": "npm run build",
11
+ "build": "tsup",
12
+ "dev": "tsup --watch"
13
+ },
14
+ "dependencies": {
15
+ "@phygitallabs/achievement": "^2.2.0",
16
+ "@phygitallabs/api-core": "^2.0.0",
17
+ "@phygitallabs/generate-certificate": "^2.0.0",
18
+ "@phygitallabs/notification-api": "^2.0.0",
19
+ "@phygitallabs/reward": "^2.0.0",
20
+ "@phygitallabs/helpers": "^2.0.0",
21
+ "@reduxjs/toolkit": "^1.9.5",
22
+ "@tanstack/react-query": "^5.66.8",
23
+ "axios": "^1.8.4",
24
+ "mem": "^10.0.0",
25
+ "posthog-js": "^1.246.0",
26
+ "react": ">=18.2",
27
+ "react-redux": "^8.0.7",
28
+ "redux-persist": "^6.0.0",
29
+ "typescript": "latest",
30
+ "uuid": "^13.0.0",
31
+ "zustand": "^5.0.8"
32
+ },
33
+ "peerDependencies": {
34
+ "firebase": "^10.0.0",
35
+ "react": ">=18.2"
36
+ },
37
+ "devDependencies": {
38
+ "@types/react": "^18.2.79",
39
+ "@types/react-redux": "^7.1.33",
40
+ "tsup": "latest",
41
+ "typescript": "latest"
42
+ }
43
+ }
@@ -0,0 +1,36 @@
1
+ import { EnvironmentType } from "../types/common";
2
+
3
+ const FIREBASE_ENV = {
4
+ dev:{
5
+ "apiKey": "AIzaSyBFReJURBrU3hyiVvHHhMKcruiyy3mkEks",
6
+ "authDomain": "phy-nomion-staging.firebaseapp.com",
7
+ "projectId": "phy-nomion-staging",
8
+ "storageBucket": "phy-nomion-staging.appspot.com",
9
+ "messagingSenderId": "445937266499",
10
+ "appId": "1:445937266499:web:9ce9b49c13dcecd9b889f6",
11
+ "measurementId": "G-GWYSS45K15"
12
+ },
13
+ staging:{
14
+ "apiKey": "AIzaSyAXrJB8n4eZyq43kb9pUSelz9vfkJLHRK8",
15
+ "authDomain": "phygital-388705.firebaseapp.com",
16
+ "projectId": "phygital-388705",
17
+ "storageBucket": "assets-fygito",
18
+ "messagingSenderId": "174350081236",
19
+ "appId": "1:174350081236:web:da29144bf47cf775a5af41",
20
+ "measurementId": "G-JVHVWXMDC9"
21
+ },
22
+ production:{
23
+ "apiKey": "AIzaSyAXrJB8n4eZyq43kb9pUSelz9vfkJLHRK8",
24
+ "authDomain": "phygital-388705.firebaseapp.com",
25
+ "projectId": "phygital-388705",
26
+ "storageBucket": "assets-fygito",
27
+ "messagingSenderId": "174350081236",
28
+ "appId": "1:174350081236:web:da29144bf47cf775a5af41",
29
+ "measurementId": "G-JVHVWXMDC9"
30
+ },
31
+ }
32
+
33
+ export const firebaseConfig = (env: EnvironmentType) => {
34
+ return FIREBASE_ENV[env];
35
+ };
36
+
@@ -0,0 +1,30 @@
1
+ const serviceApiUrl = {
2
+ dev: {
3
+ API_BASE_URL: "https://backend-dev.nomion.io",
4
+ API_BASE_CORE_URL: "https://backend-dev.nomion.io/core",
5
+ API_ACHIEVEMENT_URL: "https://backend-dev.nomion.io/achievement",
6
+ API_REWARD_URL: "https://backend-dev.nomion.io/reward",
7
+ API_GENERATE_CERTIFICATE_URL: "https://media-prc-dev.nomion.io/api",
8
+ API_NOTIFICATION_SOCKET_URL: "https://backend-dev.nomion.io/notification-ws/ws",
9
+ },
10
+ staging: {
11
+ API_BASE_URL: "https://backend-staging.nomion.io",
12
+ API_BASE_CORE_URL: "https://backend-staging.nomion.io/core",
13
+ API_ACHIEVEMENT_URL: "https://backend-staging.nomion.io/achievement",
14
+ API_REWARD_URL: "https://backend-staging.nomion.io/reward",
15
+ API_GENERATE_CERTIFICATE_URL: "https://media-prc-staging.nomion.io/api",
16
+ API_NOTIFICATION_SOCKET_URL: "https://backend-staging.nomion.io/notification-ws/ws",
17
+
18
+ },
19
+ production: {
20
+ API_BASE_URL: "https://backend.nomion.io",
21
+ API_BASE_CORE_URL: "https://backend.nomion.io/core",
22
+ API_ACHIEVEMENT_URL: "https://backend.nomion.io/achievement",
23
+ API_REWARD_URL: "https://backend.nomion.io/reward",
24
+ API_GENERATE_CERTIFICATE_URL: "https://media-prc.nomion.io/api",
25
+ API_NOTIFICATION_SOCKET_URL: "https://backend.nomion.io/notification-ws/ws",
26
+
27
+ },
28
+ }
29
+
30
+ export default serviceApiUrl;
@@ -0,0 +1,3 @@
1
+ import { cn, parse, fileToBase64 } from "@phygitallabs/helpers";
2
+
3
+ export { cn, parse, fileToBase64 };
@@ -0,0 +1 @@
1
+ export * from "./helpers";
package/src/index.ts ADDED
@@ -0,0 +1,25 @@
1
+ // Export all modules
2
+ export * from "./modules/achievement";
3
+ export * from "./modules/reward";
4
+ export * from "./modules/notification";
5
+ export * from "./modules/memory";
6
+
7
+ export * from "./modules/auth";
8
+ export * from "./modules/user-profile";
9
+
10
+ export * from "./modules/scan-chip";
11
+ export * from "./modules/campaign";
12
+
13
+ export * from "./modules/location";
14
+
15
+ export * from "./modules/generate-certificate";
16
+
17
+ export * from "./modules/data-tracking";
18
+
19
+ export * from "./providers";
20
+
21
+ export * from "./modules/achivementWithReward";
22
+
23
+ export * from "./modules/send-email";
24
+
25
+ export * from "./helper";
@@ -0,0 +1,98 @@
1
+ import { Achievement, UserAchievementProgress } from "../types";
2
+
3
+ const getLocationIdsFromAchievementRule = (achievement: Achievement) => {
4
+ if (!achievement.rule) return [];
5
+ const locationIds: string[] = [];
6
+ Object.values(achievement.rule).forEach((ruleList) => {
7
+ if (!ruleList.rules) return;
8
+ ruleList.rules.forEach((rule) => {
9
+ if (!rule.filter) return;
10
+ Object.values(rule.filter).forEach((filterList) => {
11
+ if (!filterList.filters) return;
12
+ filterList.filters.forEach((filter) => {
13
+ if (filter.label === "location_id" && filter.value) {
14
+ if (Array.isArray(filter.value)) {
15
+ locationIds.push(...filter.value);
16
+ } else {
17
+ locationIds.push(filter.value);
18
+ }
19
+ }
20
+ });
21
+ });
22
+ });
23
+ });
24
+ return Array.from(new Set(locationIds)) as string[];
25
+ };
26
+
27
+ const getActionsFromAchievementRule = (achievement: Achievement) => {
28
+ if (!achievement.rule) return [];
29
+ const actions: string[] = [];
30
+ Object.values(achievement.rule).forEach((ruleList) => {
31
+ if (!ruleList.rules) return;
32
+ ruleList.rules.forEach((rule) => {
33
+ if (rule.action) {
34
+ actions.push(rule.action)
35
+ }
36
+ });
37
+ });
38
+ return Array.from(new Set(actions)) as string[];
39
+ };
40
+
41
+ const isAchievementCompleted = (achievement: UserAchievementProgress) => {
42
+ return achievement.isCompleted || achievement.overallPercentage === 100;
43
+ }
44
+
45
+ type SnakeToCamelCase<S extends string> = S extends `${infer T}_${infer U}`
46
+ ? `${T}${Capitalize<SnakeToCamelCase<U>>}`
47
+ : S;
48
+
49
+ type ConvertSnakeToCamel<T> = T extends (infer U)[]
50
+ ? ConvertSnakeToCamel<U>[]
51
+ : T extends Record<string, unknown>
52
+ ? {
53
+ [K in keyof T as K extends string
54
+ ? SnakeToCamelCase<K>
55
+ : K]: ConvertSnakeToCamel<T[K]>;
56
+ }
57
+ : T;
58
+
59
+ /**
60
+ * Converts snake_case keys to camelCase keys in an object or array of objects
61
+ */
62
+ export function convertSnakeToCamel<T>(obj: T): ConvertSnakeToCamel<T> {
63
+ if (obj === null || obj === undefined) {
64
+ return obj as ConvertSnakeToCamel<T>;
65
+ }
66
+
67
+ if (Array.isArray(obj)) {
68
+ return obj.map((item) =>
69
+ convertSnakeToCamel(item)
70
+ ) as ConvertSnakeToCamel<T>;
71
+ }
72
+
73
+ if (typeof obj === "object" && obj.constructor === Object) {
74
+ const converted: Record<string, unknown> = {};
75
+
76
+ for (const key in obj) {
77
+ if (obj.hasOwnProperty(key)) {
78
+ const camelKey = key.replace(/_([a-z])/g, (_, letter) =>
79
+ letter.toUpperCase()
80
+ );
81
+ converted[camelKey] = convertSnakeToCamel(
82
+ (obj as Record<string, unknown>)[key]
83
+ );
84
+ }
85
+ }
86
+
87
+ return converted as ConvertSnakeToCamel<T>;
88
+ }
89
+
90
+ return obj as ConvertSnakeToCamel<T>;
91
+ }
92
+
93
+
94
+ export {
95
+ getLocationIdsFromAchievementRule,
96
+ getActionsFromAchievementRule,
97
+ isAchievementCompleted
98
+ }
@@ -0,0 +1,171 @@
1
+ import { GetManyAchievementProgressParams, GetManyDeviceUidAchievementProgressParams, GetManyUserOrDeviceAchievementProgressParams } from "@phygitallabs/achievement";
2
+
3
+ import {
4
+ GetManyAchievementProgressByDeviceParams,
5
+ } from "@phygitallabs/api-core";
6
+
7
+ import { useManyAchievementProgress as useManyAchievementProgressCore } from "@phygitallabs/achievement";
8
+ import { useManyAchievementProgressByDevice as useManyAchievementProgressByDeviceCore } from "@phygitallabs/api-core";
9
+
10
+ // Core react query hooks
11
+ import {
12
+ useAchievementProgress,
13
+ useManyAchievements,
14
+ useUserAchievementAction,
15
+ useManyChildrenAchievements,
16
+ useOneAchievement,
17
+ useManyAchievementsRewardModels,
18
+ } from "@phygitallabs/achievement";
19
+
20
+ import { isAchievementCompleted, convertSnakeToCamel } from "../helpers";
21
+
22
+ export {
23
+ useAchievementProgress,
24
+ useManyAchievements,
25
+ useUserAchievementAction,
26
+ useManyChildrenAchievements,
27
+ useOneAchievement,
28
+ useManyAchievementsRewardModels,
29
+ }
30
+
31
+ // Customize react query hooks
32
+
33
+ // Get achievement progress list by userid or deviceuid base on login status
34
+ export const useManyAchievementProgress = (
35
+ params: GetManyUserOrDeviceAchievementProgressParams,
36
+ options?: any
37
+ ) => {
38
+
39
+ const isLoggedIn = !!params.userId;
40
+
41
+ const queryParams = isLoggedIn
42
+ ? {
43
+ achievementIds: params.achievementIds,
44
+ userId: params.userId!,
45
+ applicationId: params.applicationId,
46
+ } as GetManyAchievementProgressParams
47
+ : {
48
+ achievement_ids: params.achievementIds,
49
+ device_uid: params.deviceUid!,
50
+ applicationId: params.applicationId,
51
+ } as GetManyDeviceUidAchievementProgressParams;
52
+
53
+ if (isLoggedIn) {
54
+ return useManyAchievementProgressCore(queryParams as GetManyAchievementProgressParams,
55
+ {
56
+ ...options,
57
+ select: (data) => data.map((item) => ({
58
+ ...item,
59
+ isCompleted: isAchievementCompleted(item),
60
+ })),
61
+ }
62
+ );
63
+ }
64
+
65
+ return useManyAchievementProgressByDeviceCore(
66
+ queryParams as GetManyAchievementProgressByDeviceParams, {
67
+ ...options,
68
+ select: (data) => {
69
+ const camelCaseData = convertSnakeToCamel(data);
70
+ return camelCaseData.map((item) => ({
71
+ ...item,
72
+ isCompleted: isAchievementCompleted(item),
73
+ }));
74
+ },
75
+ });
76
+ };
77
+
78
+ // export const useManyAchievementWithProgress = (
79
+ // params: UseManyAchievementWithProgressParams,
80
+ // options?: {
81
+ // achievementOptions?: Partial<UseQueryOptions>;
82
+ // progressOptions?: Partial<UseQueryOptions>;
83
+ // }
84
+ // ): UseManyAchievementWithProgressReturn => {
85
+ // // Fetch achievements with retry logic
86
+ // const achievementsQuery = useManyAchievements(
87
+ // {
88
+ // ...(params.achievementIds?.length && { ids: params.achievementIds }),
89
+ // },
90
+ // {
91
+ // retry: (failureCount: number, error: Error) => {
92
+ // // Retry up to 3 times with exponential backoff
93
+ // if (failureCount < 3) {
94
+ // console.warn(`Achievement fetch failed, retrying... (${failureCount + 1}/3)`, error);
95
+ // return true;
96
+ // }
97
+ // return false;
98
+ // },
99
+ // retryDelay: (attemptIndex: number) => Math.min(1000 * 2 ** attemptIndex, 30000),
100
+ // staleTime: 5 * 60 * 1000, // 5 minutes
101
+ // gcTime: 10 * 60 * 1000, // 10 minutes
102
+ // ...options?.achievementOptions,
103
+ // }
104
+ // );
105
+
106
+ // // Prepare achievement IDs for progress query
107
+ // const achievementIds = useMemo(() => {
108
+ // if (params.achievementIds?.length) {
109
+ // return params.achievementIds;
110
+ // }
111
+ // return achievementsQuery.data?.data?.map((a: any) => a.id) || [];
112
+ // }, [params.achievementIds, achievementsQuery.data]);
113
+
114
+ // // Fetch progress with retry logic (dependent on achievements)
115
+ // const progressQuery = useManyAchievementProgress(
116
+ // {
117
+ // achievementIds,
118
+ // userId: params.userId,
119
+ // deviceUid: params.deviceUid,
120
+ // applicationId: params.applicationId,
121
+ // },
122
+ // {
123
+ // enabled: achievementIds.length > 0
124
+ // }
125
+ // );
126
+
127
+ // // Combine data with error flags
128
+ // const combinedData = useMemo((): AchievementWithProgress[] => {
129
+ // const achievements = achievementsQuery.data?.data || [];
130
+ // const progressData = progressQuery.data || [];
131
+
132
+ // return achievements.map((achievement: any) => {
133
+ // const progress = progressData.find((p: any) => p.achievementId === achievement.id) || null;
134
+
135
+ // return {
136
+ // achievement,
137
+ // progress,
138
+ // hasAchievementError: !!achievementsQuery.error,
139
+ // hasProgressError: !!progressQuery.error,
140
+ // };
141
+ // });
142
+ // }, [achievementsQuery.data, progressQuery.data, achievementsQuery.error, progressQuery.error]);
143
+
144
+ // // Refetch functions
145
+ // const refetchAchievements = () => {
146
+ // console.log("Manually refetching achievements...");
147
+ // achievementsQuery.refetch();
148
+ // };
149
+
150
+ // const refetchProgress = () => {
151
+ // console.log("Manually refetching progress...");
152
+ // progressQuery.refetch();
153
+ // };
154
+
155
+ // const refetchAll = () => {
156
+ // console.log("Manually refetching all data...");
157
+ // Promise.all([achievementsQuery.refetch(), progressQuery.refetch()]);
158
+ // };
159
+
160
+ // return {
161
+ // data: combinedData,
162
+ // isLoading: achievementsQuery.isLoading || (achievementIds.length > 0 && progressQuery.isLoading),
163
+ // isSuccess: achievementsQuery.isSuccess && (achievementIds.length === 0 || progressQuery.isSuccess),
164
+ // achievementsError: achievementsQuery.error as Error | null,
165
+ // progressError: progressQuery.error as Error | null,
166
+ // refetchAchievements,
167
+ // refetchProgress,
168
+ // refetchAll,
169
+ // };
170
+ // };
171
+
@@ -0,0 +1,5 @@
1
+ export * from "./hooks";
2
+
3
+ export * from "./types";
4
+
5
+ export * from "./helpers";
@@ -0,0 +1,45 @@
1
+ export {
2
+ AchievementRuleActionType,
3
+ AchievementServiceProvider,
4
+ } from "@phygitallabs/achievement";
5
+
6
+ import type { Achievement, GetAchievementProgressResponse, UserAchievementProgress } from "@phygitallabs/achievement";
7
+
8
+ export {
9
+ Achievement,
10
+ GetAchievementProgressResponse,
11
+ UserAchievementProgress,
12
+ }
13
+
14
+ // Combined types for hooks
15
+ export interface AchievementWithProgress {
16
+ achievement: Achievement;
17
+ progress: UserAchievementProgress | null;
18
+ hasProgressError?: boolean;
19
+ hasAchievementError?: boolean;
20
+ }
21
+
22
+ export interface UseManyAchievementWithProgressParams {
23
+ userId?: string;
24
+ deviceUid?: string;
25
+ applicationId?: string;
26
+ achievementIds?: string[];
27
+ }
28
+
29
+ export interface UseManyAchievementWithProgressReturn {
30
+ data: AchievementWithProgress[];
31
+ isLoading: boolean;
32
+ isSuccess: boolean;
33
+ achievementsError: Error | null;
34
+ progressError: Error | null;
35
+ refetchAchievements: () => void;
36
+ refetchProgress: () => void;
37
+ refetchAll: () => void;
38
+ }
39
+
40
+ export enum AchievementType {
41
+ DEFAULT = "default",
42
+ MULTIPLE_CHECK_INS = "multiple_check_ins",
43
+ RANDOM_CHECK_INS = "random_check_ins",
44
+ GROUP_MISSION = "group_mission",
45
+ }