@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.
- package/README.md +210 -0
- package/index.ts +1 -0
- package/package.json +43 -0
- package/src/constants/firebase.ts +36 -0
- package/src/constants/service.ts +30 -0
- package/src/helper/helpers.ts +3 -0
- package/src/helper/index.ts +1 -0
- package/src/index.ts +25 -0
- package/src/modules/achievement/helpers/index.ts +98 -0
- package/src/modules/achievement/hooks/index.ts +171 -0
- package/src/modules/achievement/index.ts +5 -0
- package/src/modules/achievement/types/index.ts +45 -0
- package/src/modules/achivementWithReward/hooks/achivementPlusRewardModel.ts +83 -0
- package/src/modules/achivementWithReward/hooks/index.ts +5 -0
- package/src/modules/achivementWithReward/index.ts +5 -0
- package/src/modules/auth/README.md +527 -0
- package/src/modules/auth/constants/index.ts +9 -0
- package/src/modules/auth/helpers/index.ts +161 -0
- package/src/modules/auth/helpers/refreshToken.ts +63 -0
- package/src/modules/auth/index.ts +20 -0
- package/src/modules/auth/providers/AuthProvider.tsx +207 -0
- package/src/modules/auth/providers/index.ts +1 -0
- package/src/modules/auth/services/FirebaseAuthService.ts +290 -0
- package/src/modules/auth/services/authServiceFactory.ts +22 -0
- package/src/modules/auth/services/index.ts +3 -0
- package/src/modules/auth/store/authSlice.ts +137 -0
- package/src/modules/auth/types/index.ts +109 -0
- package/src/modules/campaign/hooks/index.ts +6 -0
- package/src/modules/campaign/hooks/useCampaignService.ts +7 -0
- package/src/modules/campaign/index.tsx +7 -0
- package/src/modules/campaign/types/campaign.ts +51 -0
- package/src/modules/campaign/types/enums.ts +4 -0
- package/src/modules/campaign/types/index.ts +4 -0
- package/src/modules/campaign/types/requests.ts +46 -0
- package/src/modules/data-tracking/hooks/index.ts +67 -0
- package/src/modules/data-tracking/index.ts +1 -0
- package/src/modules/generate-certificate/hooks/index.ts +8 -0
- package/src/modules/generate-certificate/index.ts +3 -0
- package/src/modules/generate-certificate/types/generateCertificate.ts +7 -0
- package/src/modules/generate-certificate/types/index.ts +7 -0
- package/src/modules/location/hooks/index.ts +8 -0
- package/src/modules/location/hooks/useLocationService.ts +8 -0
- package/src/modules/location/index.tsx +11 -0
- package/src/modules/location/types/index.ts +18 -0
- package/src/modules/location/types/locationModel.ts +21 -0
- package/src/modules/location/utils/index.ts +5 -0
- package/src/modules/location/utils/locationHelpers.ts +13 -0
- package/src/modules/memory/hooks/index.ts +3 -0
- package/src/modules/memory/index.ts +3 -0
- package/src/modules/memory/types/index.ts +3 -0
- package/src/modules/notification/index.ts +2 -0
- package/src/modules/notification/providers/index.tsx +50 -0
- package/src/modules/notification/types/index.ts +3 -0
- package/src/modules/reward/hooks/index.ts +14 -0
- package/src/modules/reward/hooks/useRewardService.ts +14 -0
- package/src/modules/reward/index.tsx +16 -0
- package/src/modules/reward/types/enums.ts +13 -0
- package/src/modules/reward/types/index.ts +4 -0
- package/src/modules/reward/types/requests.ts +281 -0
- package/src/modules/reward/types/reward.ts +90 -0
- package/src/modules/scan-chip/hooks/index.tsx +67 -0
- package/src/modules/scan-chip/index.ts +2 -0
- package/src/modules/scan-chip/types/index.ts +25 -0
- package/src/modules/send-email/hooks/index.ts +2 -0
- package/src/modules/send-email/index.ts +1 -0
- package/src/modules/user-profile/hooks/index.ts +3 -0
- package/src/modules/user-profile/index.ts +3 -0
- package/src/modules/user-profile/types/index.ts +3 -0
- package/src/providers/ServicesProvider.tsx +173 -0
- package/src/providers/TapquestCoreProvider.tsx +64 -0
- package/src/providers/index.ts +1 -0
- package/src/store/hooks.ts +6 -0
- package/src/store/index.ts +45 -0
- package/src/types/common.d.ts +8 -0
- package/src/types/media.ts +26 -0
- package/src/types/service.d.ts +34 -0
- package/tsconfig.json +28 -0
- package/tsup.config.ts +10 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Re-export from @phygitallabs/reward package
|
|
2
|
+
export type {
|
|
3
|
+
CmentityUserReward,
|
|
4
|
+
EntityRewardModel,
|
|
5
|
+
UserReward
|
|
6
|
+
} from "@phygitallabs/reward";
|
|
7
|
+
|
|
8
|
+
// Import EntityRewardModel for local use
|
|
9
|
+
import type { EntityRewardModel } from "@phygitallabs/reward";
|
|
10
|
+
|
|
11
|
+
// Define other types locally since they're not exported from reward package
|
|
12
|
+
export interface RewardRule {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
type: string;
|
|
17
|
+
conditions: Record<string, any>;
|
|
18
|
+
is_active: boolean;
|
|
19
|
+
priority?: number;
|
|
20
|
+
created_at: string;
|
|
21
|
+
updated_at: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface RewardGroup {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
is_active: boolean;
|
|
29
|
+
reward_ids: string[];
|
|
30
|
+
rewards?: EntityRewardModel[];
|
|
31
|
+
project_id?: string;
|
|
32
|
+
campaign_id?: string;
|
|
33
|
+
achievement_id?: string;
|
|
34
|
+
labels?: Record<string, any>;
|
|
35
|
+
custom_info?: Record<string, any>;
|
|
36
|
+
metadata?: Record<string, any>;
|
|
37
|
+
created_at: string;
|
|
38
|
+
updated_at: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface RewardNotifyTheme {
|
|
42
|
+
backgroundImage: string;
|
|
43
|
+
giftImage: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface RewardFilter {
|
|
47
|
+
user_id?: string;
|
|
48
|
+
device_uid?: string;
|
|
49
|
+
reward_model_id?: string;
|
|
50
|
+
group_reward_id?: string;
|
|
51
|
+
project_id?: string;
|
|
52
|
+
campaign_id?: string;
|
|
53
|
+
achievement_id?: string;
|
|
54
|
+
status?: string;
|
|
55
|
+
claim_status?: string;
|
|
56
|
+
is_claimable?: boolean;
|
|
57
|
+
is_expired?: boolean;
|
|
58
|
+
type?: string;
|
|
59
|
+
labels?: Record<string, any>;
|
|
60
|
+
created_from?: string;
|
|
61
|
+
created_to?: string;
|
|
62
|
+
claimed_from?: string;
|
|
63
|
+
claimed_to?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface RewardModelFilter {
|
|
67
|
+
name?: string;
|
|
68
|
+
type?: string;
|
|
69
|
+
is_active?: boolean;
|
|
70
|
+
is_claimable?: boolean;
|
|
71
|
+
project_id?: string;
|
|
72
|
+
campaign_id?: string;
|
|
73
|
+
achievement_id?: string;
|
|
74
|
+
group_reward_id?: string;
|
|
75
|
+
labels?: Record<string, any>;
|
|
76
|
+
created_from?: string;
|
|
77
|
+
created_to?: string;
|
|
78
|
+
valid_from?: string;
|
|
79
|
+
valid_until?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface ClaimUserRewardPayload {
|
|
83
|
+
certificate?: {
|
|
84
|
+
thumbnail_url?: string;
|
|
85
|
+
type?: string;
|
|
86
|
+
url?: string;
|
|
87
|
+
};
|
|
88
|
+
user_info: Record<string, any>;
|
|
89
|
+
}
|
|
90
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useChipScanStory } from "@phygitallabs/api-core";
|
|
3
|
+
import { usePGLCoreService } from "@phygitallabs/api-core";
|
|
4
|
+
import { setChipAuthToken } from "../../auth/helpers";
|
|
5
|
+
|
|
6
|
+
import { ScanChipResponse } from "../types";
|
|
7
|
+
|
|
8
|
+
interface UseScanChipProps {
|
|
9
|
+
token?: string;
|
|
10
|
+
id?: string;
|
|
11
|
+
onScanChipError?: () => void;
|
|
12
|
+
}
|
|
13
|
+
export { useChipScanStory, usePGLCoreService };
|
|
14
|
+
export function useScanChip({ token, id, onScanChipError }: UseScanChipProps): ScanChipResponse {
|
|
15
|
+
const { updateHeaders } = usePGLCoreService();
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!!token) {
|
|
19
|
+
|
|
20
|
+
const header = {
|
|
21
|
+
"Chip-Authorization": token,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
setChipAuthToken(token);
|
|
25
|
+
updateHeaders(header);
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
}, [token, updateHeaders]);
|
|
29
|
+
|
|
30
|
+
const { data, isLoading, isError, isSuccess } = useChipScanStory({
|
|
31
|
+
token,
|
|
32
|
+
id,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (isError) {
|
|
37
|
+
onScanChipError?.();
|
|
38
|
+
}
|
|
39
|
+
}, [isError, onScanChipError]);
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
if (!data)
|
|
43
|
+
return {
|
|
44
|
+
data: null,
|
|
45
|
+
isLoading,
|
|
46
|
+
isError,
|
|
47
|
+
isSuccess
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
data: {
|
|
52
|
+
externalUrl: data?.external_url,
|
|
53
|
+
campaignDetail: data?.project,
|
|
54
|
+
locationDetail: data?.location,
|
|
55
|
+
scanCounter: data?.scan_counter,
|
|
56
|
+
campaignId: data?.campaign_id,
|
|
57
|
+
chipModelId: data?.chip_model_id,
|
|
58
|
+
organizationId: data?.organization_id,
|
|
59
|
+
projectId: data?.project_id,
|
|
60
|
+
locationId: data?.location_id,
|
|
61
|
+
chipUid: data?.uid,
|
|
62
|
+
},
|
|
63
|
+
isLoading,
|
|
64
|
+
isError,
|
|
65
|
+
isSuccess
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ChipScanModel, ProjectModel, LocationModel } from "@phygitallabs/api-core";
|
|
2
|
+
|
|
3
|
+
export type ScanChipData = {
|
|
4
|
+
externalUrl?: string;
|
|
5
|
+
campaignDetail?: ProjectModel;
|
|
6
|
+
locationDetail?: LocationModel;
|
|
7
|
+
scanCounter?: number;
|
|
8
|
+
campaignId?: string;
|
|
9
|
+
chipModelId?: string;
|
|
10
|
+
organizationId?: string;
|
|
11
|
+
projectId?: string;
|
|
12
|
+
locationId?: string;
|
|
13
|
+
chipUid?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export interface ScanChipResponse {
|
|
18
|
+
data: ScanChipData | null;
|
|
19
|
+
isLoading: boolean;
|
|
20
|
+
isError: boolean;
|
|
21
|
+
isSuccess: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
export type { ChipScanModel, LocationModel, ProjectModel };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./hooks";
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import React, { useEffect, useState, useMemo } from "react";
|
|
2
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
3
|
+
import { PGLCoreServiceProvider } from "@phygitallabs/api-core";
|
|
4
|
+
import { RewardServiceProvider } from "@phygitallabs/reward";
|
|
5
|
+
import { AchievementServiceProvider } from "@phygitallabs/achievement";
|
|
6
|
+
import { GenerateCertificateServiceProvider } from "@phygitallabs/generate-certificate";
|
|
7
|
+
|
|
8
|
+
import serviceApiUrl from "../constants/service";
|
|
9
|
+
import { APIConfig, ServiceConfig } from "../types/service";
|
|
10
|
+
|
|
11
|
+
import { checkDeviceUid, getAccessToken, getRetryAttemptsRefreshToken, setRetryAttemptsRefreshToken, createRefreshTokenFunction } from "../modules/auth/helpers";
|
|
12
|
+
|
|
13
|
+
import { httpMaxRetries } from "../modules/auth";
|
|
14
|
+
|
|
15
|
+
import { useAuth } from "../modules/auth";
|
|
16
|
+
|
|
17
|
+
import axios from "axios";
|
|
18
|
+
|
|
19
|
+
interface ServicesProviderProps {
|
|
20
|
+
children: React.ReactNode;
|
|
21
|
+
queryClient: QueryClient;
|
|
22
|
+
apiConfig: APIConfig;
|
|
23
|
+
firebaseConfig?: any;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const ServicesProvider: React.FC<ServicesProviderProps> = ({
|
|
27
|
+
children,
|
|
28
|
+
queryClient,
|
|
29
|
+
apiConfig = {
|
|
30
|
+
environment: "dev",
|
|
31
|
+
version: "v1"
|
|
32
|
+
},
|
|
33
|
+
firebaseConfig
|
|
34
|
+
}) => {
|
|
35
|
+
const { refreshUser, signOut } = useAuth();
|
|
36
|
+
const { environment, version } = apiConfig;
|
|
37
|
+
const [commonServiceConfig, setCommonServiceConfig] = useState<ServiceConfig | null>(null);
|
|
38
|
+
|
|
39
|
+
// Create memoized refresh token function
|
|
40
|
+
const memoizedRefreshToken = useMemo(() => {
|
|
41
|
+
if (!firebaseConfig?.apiKey) {
|
|
42
|
+
console.warn("Firebase API key not provided, refresh token functionality will not work");
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return createRefreshTokenFunction({
|
|
46
|
+
firebaseApiKey: firebaseConfig.apiKey
|
|
47
|
+
});
|
|
48
|
+
}, [firebaseConfig?.apiKey]);
|
|
49
|
+
|
|
50
|
+
// Init client
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
const initClient = async () => {
|
|
53
|
+
try {
|
|
54
|
+
const deviceUid = await checkDeviceUid();
|
|
55
|
+
|
|
56
|
+
const responseInterceptors = ({
|
|
57
|
+
onFulfilled: (response: any) => response,
|
|
58
|
+
onRejected: async (error: any) => {
|
|
59
|
+
|
|
60
|
+
// TODO: Remove
|
|
61
|
+
// Feature: use refresh token to get new access token when token expired, have maximum `retry attempts`
|
|
62
|
+
const originalRequest = error.config;
|
|
63
|
+
|
|
64
|
+
if (error.response?.status === 401 && !originalRequest._retry) {
|
|
65
|
+
const retryAttempts = parseInt(
|
|
66
|
+
getRetryAttemptsRefreshToken() || "0",
|
|
67
|
+
10
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (retryAttempts >= httpMaxRetries) {
|
|
71
|
+
await signOut();
|
|
72
|
+
return Promise.reject(error);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
setRetryAttemptsRefreshToken(`${retryAttempts + 1}`);
|
|
76
|
+
originalRequest._retry = true;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
if (!memoizedRefreshToken) {
|
|
80
|
+
await signOut();
|
|
81
|
+
return Promise.reject(error);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const result = await memoizedRefreshToken();
|
|
85
|
+
|
|
86
|
+
if (result?.accessToken) {
|
|
87
|
+
originalRequest.headers[
|
|
88
|
+
"Authorization"
|
|
89
|
+
] = `Bearer ${result.accessToken}`;
|
|
90
|
+
|
|
91
|
+
setRetryAttemptsRefreshToken("0");
|
|
92
|
+
|
|
93
|
+
refreshUser(result);
|
|
94
|
+
|
|
95
|
+
return axios(originalRequest);
|
|
96
|
+
}
|
|
97
|
+
} catch (refreshError) {
|
|
98
|
+
console.log("Failed to refresh token:", refreshError);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return Promise.reject(error);
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const requestInterceptors = ({
|
|
107
|
+
onFulfilled: (config: any) => {
|
|
108
|
+
// Feature: set access token to request header
|
|
109
|
+
const currentToken = getAccessToken();
|
|
110
|
+
|
|
111
|
+
if (currentToken && !config.headers.Authorization) {
|
|
112
|
+
config.headers.Authorization = `Bearer ${currentToken}`;
|
|
113
|
+
}
|
|
114
|
+
return config;
|
|
115
|
+
},
|
|
116
|
+
onRejected: (error: any) => Promise.reject(error),
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const axiosConfig = {
|
|
120
|
+
headers: {
|
|
121
|
+
"Content-Type": "application/json",
|
|
122
|
+
"Device-UID": deviceUid,
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const config: ServiceConfig = {
|
|
127
|
+
queryClient,
|
|
128
|
+
axiosConfig,
|
|
129
|
+
responseInterceptors,
|
|
130
|
+
requestInterceptors,
|
|
131
|
+
useDevTool: true,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
setCommonServiceConfig(config);
|
|
135
|
+
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error(error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
initClient();
|
|
142
|
+
}, [queryClient]);
|
|
143
|
+
|
|
144
|
+
if (!commonServiceConfig) {
|
|
145
|
+
return <></>
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<PGLCoreServiceProvider
|
|
150
|
+
{...commonServiceConfig}
|
|
151
|
+
baseURL={`${serviceApiUrl[environment].API_BASE_URL}/${version}`}
|
|
152
|
+
baseCoreURL={`${serviceApiUrl[environment].API_BASE_CORE_URL}/${version}`}
|
|
153
|
+
>
|
|
154
|
+
<RewardServiceProvider
|
|
155
|
+
{...commonServiceConfig}
|
|
156
|
+
// baseURL={`${serviceApiUrl[environment].API_REWARD_URL}/${version}`}
|
|
157
|
+
baseURL={`${serviceApiUrl[environment].API_REWARD_URL}/v1`} // todo: using v1 until backend fully migrate
|
|
158
|
+
>
|
|
159
|
+
<AchievementServiceProvider
|
|
160
|
+
{...commonServiceConfig}
|
|
161
|
+
baseURL={`${serviceApiUrl[environment].API_ACHIEVEMENT_URL}/${version}`}
|
|
162
|
+
>
|
|
163
|
+
<GenerateCertificateServiceProvider
|
|
164
|
+
{...commonServiceConfig}
|
|
165
|
+
baseURL={`${serviceApiUrl[environment].API_GENERATE_CERTIFICATE_URL}/v1`}
|
|
166
|
+
>
|
|
167
|
+
{children}
|
|
168
|
+
</GenerateCertificateServiceProvider>
|
|
169
|
+
</AchievementServiceProvider>
|
|
170
|
+
</RewardServiceProvider>
|
|
171
|
+
</PGLCoreServiceProvider>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
import { Provider } from "react-redux";
|
|
3
|
+
import { PersistGate } from "redux-persist/integration/react";
|
|
4
|
+
|
|
5
|
+
import { ServicesProvider } from "./ServicesProvider";
|
|
6
|
+
import { APIConfig } from "../types/service";
|
|
7
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
8
|
+
|
|
9
|
+
import { store, persistor } from "../store";
|
|
10
|
+
import { AuthCallbacks } from "../modules/auth/types";
|
|
11
|
+
import { AuthProvider } from "../modules/auth/providers";
|
|
12
|
+
import { FirebaseConfig } from "../modules/auth/types";
|
|
13
|
+
|
|
14
|
+
interface TapquestCoreProviderProps {
|
|
15
|
+
children: React.ReactNode;
|
|
16
|
+
queryClient: QueryClient;
|
|
17
|
+
apiConfig: APIConfig;
|
|
18
|
+
firebaseConfig?: FirebaseConfig;
|
|
19
|
+
authCallbacks?: AuthCallbacks;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const TapquestCoreProvider: React.FC<TapquestCoreProviderProps> = ({
|
|
23
|
+
children,
|
|
24
|
+
queryClient,
|
|
25
|
+
apiConfig,
|
|
26
|
+
firebaseConfig,
|
|
27
|
+
authCallbacks
|
|
28
|
+
}) => {
|
|
29
|
+
|
|
30
|
+
const internalAuthService = useMemo(() => {
|
|
31
|
+
if (firebaseConfig && typeof window !== "undefined") {
|
|
32
|
+
try {
|
|
33
|
+
// Dynamic import to avoid loading Firebase modules during build
|
|
34
|
+
const { createAuthService } = require("../modules/auth/services");
|
|
35
|
+
return createAuthService({ firebaseConfig });
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.warn("Failed to create auth service from firebase config:", error);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return null;
|
|
43
|
+
}, [firebaseConfig]);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Provider store={store}>
|
|
47
|
+
<PersistGate loading={null} persistor={persistor}>
|
|
48
|
+
<AuthProvider
|
|
49
|
+
authService={internalAuthService}
|
|
50
|
+
authCallbacks={authCallbacks}
|
|
51
|
+
>
|
|
52
|
+
<ServicesProvider
|
|
53
|
+
queryClient={queryClient}
|
|
54
|
+
apiConfig={apiConfig}
|
|
55
|
+
firebaseConfig={firebaseConfig}
|
|
56
|
+
>
|
|
57
|
+
{children}
|
|
58
|
+
</ServicesProvider>
|
|
59
|
+
</AuthProvider>
|
|
60
|
+
|
|
61
|
+
</PersistGate>
|
|
62
|
+
</Provider>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './TapquestCoreProvider';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
|
2
|
+
import type { TapquestCoreRootState, TapquestCoreAppDispatch } from './index';
|
|
3
|
+
|
|
4
|
+
// Typed hooks for internal use within tapquest-core
|
|
5
|
+
export const useAppDispatch = () => useDispatch<TapquestCoreAppDispatch>();
|
|
6
|
+
export const useAppSelector: TypedUseSelectorHook<TapquestCoreRootState> = useSelector;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { configureStore } from "@reduxjs/toolkit";
|
|
2
|
+
import storage from "redux-persist/lib/storage";
|
|
3
|
+
import { persistReducer, persistStore, PersistConfig } from "redux-persist";
|
|
4
|
+
import { authSlice } from "../modules/auth/store/authSlice";
|
|
5
|
+
|
|
6
|
+
// Auth persist config
|
|
7
|
+
const authPersistConfig: PersistConfig<any> = {
|
|
8
|
+
key: "tapquest-auth",
|
|
9
|
+
storage,
|
|
10
|
+
whitelist: ["user", "isSignedIn"], // Only persist user and sign-in status
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// Create persisted auth reducer
|
|
14
|
+
const persistedAuthReducer = persistReducer(authPersistConfig, authSlice.reducer);
|
|
15
|
+
|
|
16
|
+
// Configure the tapquest-core store
|
|
17
|
+
export const store = configureStore({
|
|
18
|
+
reducer: {
|
|
19
|
+
auth: persistedAuthReducer,
|
|
20
|
+
},
|
|
21
|
+
middleware: (getDefaultMiddleware) =>
|
|
22
|
+
getDefaultMiddleware({
|
|
23
|
+
serializableCheck: {
|
|
24
|
+
// Ignore redux-persist actions
|
|
25
|
+
ignoredActions: [
|
|
26
|
+
'persist/FLUSH',
|
|
27
|
+
'persist/REHYDRATE',
|
|
28
|
+
'persist/PAUSE',
|
|
29
|
+
'persist/PERSIST',
|
|
30
|
+
'persist/PURGE',
|
|
31
|
+
'persist/REGISTER',
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Create persistor
|
|
38
|
+
export const persistor = persistStore(store);
|
|
39
|
+
|
|
40
|
+
// Export types
|
|
41
|
+
export type TapquestCoreRootState = ReturnType<typeof store.getState>;
|
|
42
|
+
export type TapquestCoreAppDispatch = typeof store.dispatch;
|
|
43
|
+
|
|
44
|
+
// Export store instance for internal use
|
|
45
|
+
export { store as tapquestCoreStore };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export enum MediaType {
|
|
2
|
+
IMAGE = "image",
|
|
3
|
+
VIDEO = "video",
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type DateTimeNumber = number;
|
|
7
|
+
|
|
8
|
+
export interface Media {
|
|
9
|
+
url: string;
|
|
10
|
+
type: MediaType;
|
|
11
|
+
thumbnail_url?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export enum EntityStatus {
|
|
15
|
+
ACTIVE = "Active",
|
|
16
|
+
INACTIVE = "Inactive",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CommonModel {
|
|
20
|
+
id: string;
|
|
21
|
+
status: EntityStatus;
|
|
22
|
+
created_at: DateTimeNumber;
|
|
23
|
+
updated_at: DateTimeNumber;
|
|
24
|
+
created_by?: string;
|
|
25
|
+
updated_by?: string;
|
|
26
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
2
|
+
import { InternalAxiosRequestConfig } from "axios";
|
|
3
|
+
|
|
4
|
+
import { EnvironmentType } from "./common";
|
|
5
|
+
|
|
6
|
+
export interface RequestInterceptor {
|
|
7
|
+
onFulfilled?: (
|
|
8
|
+
config: InternalAxiosRequestConfig
|
|
9
|
+
) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;
|
|
10
|
+
onRejected?: (error: any) => any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ResponseInterceptor {
|
|
14
|
+
onFulfilled?: (response: any) => any;
|
|
15
|
+
onRejected?: (error: any) => any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface APIConfig {
|
|
19
|
+
environment: EnvironmentType;
|
|
20
|
+
version: APIVersionType;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ServiceConfig {
|
|
24
|
+
queryClient: QueryClient;
|
|
25
|
+
axiosConfig: {
|
|
26
|
+
headers: {
|
|
27
|
+
"Content-Type": string;
|
|
28
|
+
"Device-UID": string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
responseInterceptors: any;
|
|
32
|
+
requestInterceptors: any;
|
|
33
|
+
useDevTool: boolean;
|
|
34
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2021", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"allowJs": false,
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
|
+
"baseUrl": ".",
|
|
17
|
+
"paths": {
|
|
18
|
+
"react": ["./node_modules/@types/react"]
|
|
19
|
+
},
|
|
20
|
+
/* Linting */
|
|
21
|
+
"strict": true,
|
|
22
|
+
"noUnusedLocals": true,
|
|
23
|
+
"noUnusedParameters": true,
|
|
24
|
+
"noFallthroughCasesInSwitch": true
|
|
25
|
+
},
|
|
26
|
+
"exclude": ["node_modules", "dist"],
|
|
27
|
+
"include": ["."]
|
|
28
|
+
}
|