@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,290 @@
|
|
|
1
|
+
// Client-side only imports
|
|
2
|
+
let firebaseApp: any;
|
|
3
|
+
let firebaseAuth: any;
|
|
4
|
+
|
|
5
|
+
// Dynamic imports for client-side only
|
|
6
|
+
const getFirebaseModules = async () => {
|
|
7
|
+
if (typeof window === 'undefined') {
|
|
8
|
+
throw new Error('Firebase can only be used in client environment');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (!firebaseApp || !firebaseAuth) {
|
|
12
|
+
const [
|
|
13
|
+
{ initializeApp, getApps },
|
|
14
|
+
{
|
|
15
|
+
getAuth,
|
|
16
|
+
signInWithEmailAndPassword,
|
|
17
|
+
createUserWithEmailAndPassword,
|
|
18
|
+
signInWithPopup,
|
|
19
|
+
GoogleAuthProvider,
|
|
20
|
+
signOut,
|
|
21
|
+
sendPasswordResetEmail,
|
|
22
|
+
sendEmailVerification,
|
|
23
|
+
updatePassword,
|
|
24
|
+
onAuthStateChanged,
|
|
25
|
+
}
|
|
26
|
+
] = await Promise.all([
|
|
27
|
+
import("firebase/app"),
|
|
28
|
+
import("firebase/auth")
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
firebaseApp = { initializeApp, getApps };
|
|
32
|
+
firebaseAuth = {
|
|
33
|
+
getAuth,
|
|
34
|
+
signInWithEmailAndPassword,
|
|
35
|
+
createUserWithEmailAndPassword,
|
|
36
|
+
signInWithPopup,
|
|
37
|
+
GoogleAuthProvider,
|
|
38
|
+
signOut,
|
|
39
|
+
sendPasswordResetEmail,
|
|
40
|
+
sendEmailVerification,
|
|
41
|
+
updatePassword,
|
|
42
|
+
onAuthStateChanged,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { firebaseApp, firebaseAuth };
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
import { AuthService, FirebaseConfig, AuthResponse, UserData, SignInProvider } from "../types";
|
|
50
|
+
import { accessTokenKey, refreshTokenKey } from "../constants";
|
|
51
|
+
|
|
52
|
+
export class FirebaseAuthService implements AuthService {
|
|
53
|
+
private app: any;
|
|
54
|
+
private auth: any;
|
|
55
|
+
private googleProvider: any;
|
|
56
|
+
private config: FirebaseConfig;
|
|
57
|
+
private initialized = false;
|
|
58
|
+
|
|
59
|
+
constructor(config: FirebaseConfig) {
|
|
60
|
+
this.config = config;
|
|
61
|
+
// Initialization will be done lazily in client environment
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private async ensureInitialized() {
|
|
65
|
+
if (this.initialized) return;
|
|
66
|
+
|
|
67
|
+
const { firebaseApp, firebaseAuth } = await getFirebaseModules();
|
|
68
|
+
|
|
69
|
+
// Initialize Firebase app if not already initialized
|
|
70
|
+
if (!firebaseApp.getApps().length) {
|
|
71
|
+
this.app = firebaseApp.initializeApp(this.config);
|
|
72
|
+
} else {
|
|
73
|
+
this.app = firebaseApp.getApps()[0];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.auth = firebaseAuth.getAuth(this.app);
|
|
77
|
+
this.googleProvider = new firebaseAuth.GoogleAuthProvider();
|
|
78
|
+
this.initialized = true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private transformUserData(user: any): UserData {
|
|
82
|
+
return {
|
|
83
|
+
id: user.uid,
|
|
84
|
+
uid: user.uid,
|
|
85
|
+
userName: user.displayName || "",
|
|
86
|
+
displayName: user.displayName || "",
|
|
87
|
+
email: user.email || "",
|
|
88
|
+
refreshToken: user.refreshToken,
|
|
89
|
+
accessToken: (user as any).accessToken || "",
|
|
90
|
+
exp: (user as any).stsTokenManager?.expirationTime || 0,
|
|
91
|
+
emailVerified: user.emailVerified,
|
|
92
|
+
avatar: user.photoURL || "/images/default-avatar.jpg",
|
|
93
|
+
signInProvider: this.getSignInProvider(user),
|
|
94
|
+
role: undefined,
|
|
95
|
+
scanStatus: false,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private getSignInProvider(user: any): SignInProvider {
|
|
100
|
+
const providers = user.providerData.map((p: any) => p.providerId);
|
|
101
|
+
if (providers.includes("google.com")) return "google.com";
|
|
102
|
+
return "password";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private translateErrorCode(errorCode: string): string {
|
|
106
|
+
switch (errorCode) {
|
|
107
|
+
case "auth/invalid-email":
|
|
108
|
+
return "Email không hợp lệ";
|
|
109
|
+
case "auth/user-disabled":
|
|
110
|
+
return "Tài khoản đã bị khóa";
|
|
111
|
+
case "auth/wrong-password":
|
|
112
|
+
return "Tài khoản/mật khẩu không đúng";
|
|
113
|
+
case "auth/user-not-found":
|
|
114
|
+
return "Tài khoản/mật khẩu không đúng";
|
|
115
|
+
case "auth/weak-password":
|
|
116
|
+
return "Weak password! Please use stronger password.";
|
|
117
|
+
case "auth/email-already-in-use":
|
|
118
|
+
return "Email đã được sử dụng";
|
|
119
|
+
case "auth/account-exists-with-different-credential":
|
|
120
|
+
return "Tài khoản email đã được sử dụng bởi một phương thức đăng nhập khác";
|
|
121
|
+
case "auth/email-not-verified":
|
|
122
|
+
return "Email chưa được xác thực";
|
|
123
|
+
default:
|
|
124
|
+
return "Đã có lỗi xảy ra";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async signInWithEmailAndPassword(email: string, password: string): Promise<AuthResponse> {
|
|
129
|
+
try {
|
|
130
|
+
await this.ensureInitialized();
|
|
131
|
+
const { firebaseAuth } = await getFirebaseModules();
|
|
132
|
+
const signInResponse = await firebaseAuth.signInWithEmailAndPassword(this.auth, email, password);
|
|
133
|
+
const { user } = signInResponse;
|
|
134
|
+
const data = this.transformUserData(user);
|
|
135
|
+
|
|
136
|
+
const { emailVerified } = data;
|
|
137
|
+
if (!emailVerified) {
|
|
138
|
+
await this.signOut();
|
|
139
|
+
localStorage.removeItem("phygital-user-info");
|
|
140
|
+
localStorage.removeItem(accessTokenKey);
|
|
141
|
+
localStorage.removeItem(refreshTokenKey);
|
|
142
|
+
throw new Error("Email is not verified");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
errorCode: "",
|
|
147
|
+
data,
|
|
148
|
+
};
|
|
149
|
+
} catch (err: any) {
|
|
150
|
+
const errorCode = this.translateErrorCode(err.code);
|
|
151
|
+
return {
|
|
152
|
+
errorCode,
|
|
153
|
+
data: null,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async signInWithGoogle(): Promise<AuthResponse> {
|
|
159
|
+
try {
|
|
160
|
+
await this.ensureInitialized();
|
|
161
|
+
const { firebaseAuth } = await getFirebaseModules();
|
|
162
|
+
const signInResponse = await firebaseAuth.signInWithPopup(this.auth, this.googleProvider);
|
|
163
|
+
const { user } = signInResponse;
|
|
164
|
+
const data = this.transformUserData(user);
|
|
165
|
+
|
|
166
|
+
const { emailVerified } = data;
|
|
167
|
+
if (!emailVerified) {
|
|
168
|
+
await this.signOut();
|
|
169
|
+
throw new Error("Email is not verified");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
errorCode: "",
|
|
174
|
+
data,
|
|
175
|
+
};
|
|
176
|
+
} catch (err: any) {
|
|
177
|
+
const errorCode = this.translateErrorCode(err.code);
|
|
178
|
+
return {
|
|
179
|
+
errorCode,
|
|
180
|
+
data: null,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async signUp(email: string, password: string): Promise<AuthResponse> {
|
|
186
|
+
try {
|
|
187
|
+
await this.ensureInitialized();
|
|
188
|
+
const { firebaseAuth } = await getFirebaseModules();
|
|
189
|
+
const signUpResponse = await firebaseAuth.createUserWithEmailAndPassword(this.auth, email, password);
|
|
190
|
+
const { user } = signUpResponse;
|
|
191
|
+
const data = this.transformUserData(user);
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
errorCode: "",
|
|
195
|
+
data,
|
|
196
|
+
};
|
|
197
|
+
} catch (err: any) {
|
|
198
|
+
const errorCode = this.translateErrorCode(err.code);
|
|
199
|
+
return {
|
|
200
|
+
errorCode,
|
|
201
|
+
data: null,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async signOut(): Promise<void> {
|
|
207
|
+
try {
|
|
208
|
+
await this.ensureInitialized();
|
|
209
|
+
const { firebaseAuth } = await getFirebaseModules();
|
|
210
|
+
await firebaseAuth.signOut(this.auth);
|
|
211
|
+
// Tracking is now handled through callbacks in the consuming app
|
|
212
|
+
} catch (err) {
|
|
213
|
+
console.log("Firebase signOut error:", err);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async sendPasswordResetEmail(email: string): Promise<void> {
|
|
218
|
+
await this.ensureInitialized();
|
|
219
|
+
const { firebaseAuth } = await getFirebaseModules();
|
|
220
|
+
const actionCodeSettings = {
|
|
221
|
+
url: window.location.origin,
|
|
222
|
+
handleCodeInApp: true,
|
|
223
|
+
};
|
|
224
|
+
await firebaseAuth.sendPasswordResetEmail(this.auth, email, actionCodeSettings);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async sendEmailVerification(): Promise<void> {
|
|
228
|
+
await this.ensureInitialized();
|
|
229
|
+
const { firebaseAuth } = await getFirebaseModules();
|
|
230
|
+
if (!this.auth.currentUser) {
|
|
231
|
+
throw new Error("No current user");
|
|
232
|
+
}
|
|
233
|
+
const actionCodeSettings = {
|
|
234
|
+
url: window.location.origin,
|
|
235
|
+
handleCodeInApp: true,
|
|
236
|
+
};
|
|
237
|
+
await firebaseAuth.sendEmailVerification(this.auth.currentUser, actionCodeSettings);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async changePassword(newPassword: string): Promise<void> {
|
|
241
|
+
await this.ensureInitialized();
|
|
242
|
+
const { firebaseAuth } = await getFirebaseModules();
|
|
243
|
+
const user = this.auth.currentUser;
|
|
244
|
+
if (!user) {
|
|
245
|
+
throw new Error("No current user");
|
|
246
|
+
}
|
|
247
|
+
return firebaseAuth.updatePassword(user, newPassword);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
onAuthStateChanged(callback: (user: UserData | null) => void): () => void {
|
|
251
|
+
// For client-side only usage, ensure initialization before setting up auth state listener
|
|
252
|
+
if (typeof window === 'undefined') {
|
|
253
|
+
return () => { }; // Return empty unsubscriber for server-side
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Set up the listener asynchronously
|
|
257
|
+
let unsubscriber: (() => void) | null = null;
|
|
258
|
+
|
|
259
|
+
this.ensureInitialized().then(async () => {
|
|
260
|
+
const { firebaseAuth } = await getFirebaseModules();
|
|
261
|
+
unsubscriber = firebaseAuth.onAuthStateChanged(this.auth, (user: any) => {
|
|
262
|
+
if (user && user.emailVerified) {
|
|
263
|
+
const userData = this.transformUserData(user);
|
|
264
|
+
callback(userData);
|
|
265
|
+
} else {
|
|
266
|
+
callback(null);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Return unsubscriber function
|
|
272
|
+
return () => {
|
|
273
|
+
if (unsubscriber) {
|
|
274
|
+
unsubscriber();
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
getCurrentUser(): UserData | null {
|
|
280
|
+
if (typeof window === 'undefined' || !this.initialized) {
|
|
281
|
+
return null; // Return null for server-side or before initialization
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const user = this.auth.currentUser;
|
|
285
|
+
if (user && user.emailVerified) {
|
|
286
|
+
return this.transformUserData(user);
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FirebaseAuthService } from "./FirebaseAuthService";
|
|
2
|
+
|
|
3
|
+
interface AuthServiceConfig {
|
|
4
|
+
firebaseConfig: any;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
let authServiceInstance: FirebaseAuthService | null = null;
|
|
8
|
+
|
|
9
|
+
export const createAuthService = (config: AuthServiceConfig): FirebaseAuthService => {
|
|
10
|
+
if (!authServiceInstance) {
|
|
11
|
+
authServiceInstance = new FirebaseAuthService(config.firebaseConfig);
|
|
12
|
+
}
|
|
13
|
+
return authServiceInstance;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const getAuthService = (): FirebaseAuthService | null => {
|
|
17
|
+
return authServiceInstance;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const resetAuthService = (): void => {
|
|
21
|
+
authServiceInstance = null;
|
|
22
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|
2
|
+
import { UserData, UserRole } from "../types";
|
|
3
|
+
import {
|
|
4
|
+
setUserInfo,
|
|
5
|
+
removeUserInfo,
|
|
6
|
+
getUserInfo,
|
|
7
|
+
setAccessToken,
|
|
8
|
+
setRefreshToken,
|
|
9
|
+
removeAccessToken,
|
|
10
|
+
removeRefreshToken,
|
|
11
|
+
getAccessToken,
|
|
12
|
+
} from "../helpers";
|
|
13
|
+
|
|
14
|
+
const defaultUser: UserData = {
|
|
15
|
+
uid: "",
|
|
16
|
+
id: "",
|
|
17
|
+
userName: "",
|
|
18
|
+
displayName: "",
|
|
19
|
+
avatar: "/images/default-avatar.jpg",
|
|
20
|
+
email: "",
|
|
21
|
+
exp: 0,
|
|
22
|
+
emailVerified: false,
|
|
23
|
+
refreshToken: "",
|
|
24
|
+
accessToken: "",
|
|
25
|
+
role: UserRole.NULL,
|
|
26
|
+
scanStatus: false,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export interface AuthState {
|
|
30
|
+
user: UserData;
|
|
31
|
+
isSignedIn: boolean;
|
|
32
|
+
pending: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const initialState: AuthState = {
|
|
36
|
+
user: defaultUser,
|
|
37
|
+
isSignedIn: false,
|
|
38
|
+
pending: true,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const authSlice = createSlice<
|
|
42
|
+
AuthState,
|
|
43
|
+
{
|
|
44
|
+
signIn: (state: AuthState, action: PayloadAction<UserData>) => void;
|
|
45
|
+
signOut: (state: AuthState) => void;
|
|
46
|
+
updateScanStatus: (state: AuthState, action: PayloadAction<boolean>) => void;
|
|
47
|
+
refreshUser: (state: AuthState, action: PayloadAction<UserData>) => void;
|
|
48
|
+
setPending: (state: AuthState, action: PayloadAction<boolean>) => void;
|
|
49
|
+
initializeFromStorage: (state: AuthState) => void;
|
|
50
|
+
},
|
|
51
|
+
'auth'
|
|
52
|
+
>({
|
|
53
|
+
name: "auth",
|
|
54
|
+
initialState,
|
|
55
|
+
reducers: {
|
|
56
|
+
signIn: (state, action: PayloadAction<UserData>) => {
|
|
57
|
+
const userData = action.payload;
|
|
58
|
+
state.user = userData;
|
|
59
|
+
state.isSignedIn = true;
|
|
60
|
+
state.pending = false;
|
|
61
|
+
|
|
62
|
+
// Store in localStorage for persistence
|
|
63
|
+
setUserInfo(userData);
|
|
64
|
+
setAccessToken(userData.accessToken);
|
|
65
|
+
setRefreshToken(userData.refreshToken);
|
|
66
|
+
},
|
|
67
|
+
signOut: (state) => {
|
|
68
|
+
state.user = defaultUser;
|
|
69
|
+
state.isSignedIn = false;
|
|
70
|
+
state.pending = false;
|
|
71
|
+
|
|
72
|
+
// Clear localStorage
|
|
73
|
+
removeUserInfo();
|
|
74
|
+
removeAccessToken();
|
|
75
|
+
removeRefreshToken();
|
|
76
|
+
},
|
|
77
|
+
updateScanStatus: (state, action: PayloadAction<boolean>) => {
|
|
78
|
+
state.user.scanStatus = action.payload;
|
|
79
|
+
|
|
80
|
+
// Update localStorage
|
|
81
|
+
setUserInfo(state.user);
|
|
82
|
+
},
|
|
83
|
+
refreshUser: (state, action: PayloadAction<UserData>) => {
|
|
84
|
+
const userData = action.payload;
|
|
85
|
+
if (userData) {
|
|
86
|
+
state.user = userData;
|
|
87
|
+
state.isSignedIn = true;
|
|
88
|
+
state.pending = false;
|
|
89
|
+
|
|
90
|
+
// Update localStorage
|
|
91
|
+
setUserInfo(userData);
|
|
92
|
+
setAccessToken(userData.accessToken);
|
|
93
|
+
setRefreshToken(userData.refreshToken);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
setPending: (state, action: PayloadAction<boolean>) => {
|
|
97
|
+
state.pending = action.payload;
|
|
98
|
+
},
|
|
99
|
+
initializeFromStorage: (state) => {
|
|
100
|
+
const storedUser = getUserInfo();
|
|
101
|
+
const accessToken = getAccessToken();
|
|
102
|
+
|
|
103
|
+
if (storedUser && accessToken) {
|
|
104
|
+
state.user = storedUser;
|
|
105
|
+
state.isSignedIn = true;
|
|
106
|
+
}
|
|
107
|
+
state.pending = false;
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
export const {
|
|
113
|
+
signIn,
|
|
114
|
+
signOut,
|
|
115
|
+
updateScanStatus,
|
|
116
|
+
refreshUser,
|
|
117
|
+
setPending,
|
|
118
|
+
initializeFromStorage,
|
|
119
|
+
} = authSlice.actions;
|
|
120
|
+
|
|
121
|
+
// Selectors
|
|
122
|
+
export const selectAuth = (state: any) => state.auth;
|
|
123
|
+
export const selectUser = (state: any) => state.auth.user;
|
|
124
|
+
export const selectUserUId = (state: any) =>
|
|
125
|
+
state.auth.user.uid || state.auth.user.id;
|
|
126
|
+
export const selectUserAccesstoken = (state: any) =>
|
|
127
|
+
state.auth.user.accessToken;
|
|
128
|
+
export const selectUserEmail = (state: any) => state.auth.user.email;
|
|
129
|
+
export const selectUsername = (state: any) => state.auth.user.userName;
|
|
130
|
+
export const selectSignInProvider = (state: any) =>
|
|
131
|
+
state.auth.user.signInProvider;
|
|
132
|
+
export const selectUserScanStatus = (state: any) =>
|
|
133
|
+
state.auth.user.scanStatus;
|
|
134
|
+
export const selectIsSignedIn = (state: any) => state.auth.isSignedIn;
|
|
135
|
+
export const selectIsLoading = (state: any) => state.auth.pending;
|
|
136
|
+
|
|
137
|
+
export default authSlice.reducer;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export type SignInProvider = "password" | "google.com";
|
|
2
|
+
|
|
3
|
+
export enum UserRole {
|
|
4
|
+
NULL = "NULL",
|
|
5
|
+
SUPERADMIN = "SUPER_ADMIN",
|
|
6
|
+
ORGADMIN = "ORG_ADMIN",
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type UserData = {
|
|
10
|
+
uid: string;
|
|
11
|
+
id: string;
|
|
12
|
+
userName: string;
|
|
13
|
+
displayName?: string;
|
|
14
|
+
avatar: string;
|
|
15
|
+
exp: number;
|
|
16
|
+
email: string;
|
|
17
|
+
emailVerified: boolean;
|
|
18
|
+
accessToken: string;
|
|
19
|
+
refreshToken: string;
|
|
20
|
+
signInProvider?: SignInProvider;
|
|
21
|
+
role?: UserRole;
|
|
22
|
+
scanStatus?: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export interface UpdateUser {
|
|
26
|
+
full_name: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface AuthResponse {
|
|
30
|
+
errorCode: string;
|
|
31
|
+
data: UserData | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface AuthState {
|
|
35
|
+
user: UserData;
|
|
36
|
+
isSignedIn: boolean;
|
|
37
|
+
pending: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Auth service interface - allows for Firebase or other providers
|
|
41
|
+
export interface AuthService {
|
|
42
|
+
// Authentication methods
|
|
43
|
+
signInWithEmailAndPassword(email: string, password: string): Promise<AuthResponse>;
|
|
44
|
+
signInWithGoogle(): Promise<AuthResponse>;
|
|
45
|
+
signUp(email: string, password: string): Promise<AuthResponse>;
|
|
46
|
+
signOut(): Promise<void>;
|
|
47
|
+
|
|
48
|
+
// Password management
|
|
49
|
+
sendPasswordResetEmail(email: string): Promise<void>;
|
|
50
|
+
sendEmailVerification(): Promise<void>;
|
|
51
|
+
changePassword(newPassword: string): Promise<void>;
|
|
52
|
+
|
|
53
|
+
// Auth state monitoring
|
|
54
|
+
onAuthStateChanged(callback: (user: UserData | null) => void): () => void;
|
|
55
|
+
|
|
56
|
+
// Current user
|
|
57
|
+
getCurrentUser(): UserData | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Auth callbacks interface for external integrations (analytics, tracking, etc.)
|
|
61
|
+
export interface AuthCallbacks {
|
|
62
|
+
onUserIdentify?: (userInfo: { email: string; name?: string; avatar?: string; uid?: string }) => void;
|
|
63
|
+
onTrackingReset?: () => void;
|
|
64
|
+
onSignInSuccess?: (userData: UserData) => void;
|
|
65
|
+
onSignInError?: (error: string) => void;
|
|
66
|
+
onSignOutSuccess?: () => void;
|
|
67
|
+
onSignUpSuccess?: (userData: UserData) => void;
|
|
68
|
+
onSignUpError?: (error: string) => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Hook return types
|
|
72
|
+
export interface UseAuthReturn {
|
|
73
|
+
// User state
|
|
74
|
+
user: UserData;
|
|
75
|
+
isSignedIn: boolean;
|
|
76
|
+
isLoading: boolean;
|
|
77
|
+
|
|
78
|
+
// Auth actions
|
|
79
|
+
signIn: (email: string, password: string) => Promise<AuthResponse>;
|
|
80
|
+
signInWithGoogle: () => Promise<AuthResponse>;
|
|
81
|
+
signUp: (email: string, password: string) => Promise<AuthResponse>;
|
|
82
|
+
signOut: () => Promise<void>;
|
|
83
|
+
|
|
84
|
+
// Password actions
|
|
85
|
+
forgotPassword: (email: string) => Promise<void>;
|
|
86
|
+
sendEmailVerification: () => Promise<void>;
|
|
87
|
+
changePassword: (newPassword: string) => Promise<void>;
|
|
88
|
+
|
|
89
|
+
// User management
|
|
90
|
+
updateScanStatus: (status: boolean) => void;
|
|
91
|
+
refreshUser: (userData: UserData) => void;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Provider props
|
|
95
|
+
export interface AuthProviderProps {
|
|
96
|
+
children: React.ReactNode;
|
|
97
|
+
authService: AuthService;
|
|
98
|
+
authCallbacks?: AuthCallbacks;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface FirebaseConfig {
|
|
102
|
+
apiKey: string;
|
|
103
|
+
authDomain: string;
|
|
104
|
+
projectId: string;
|
|
105
|
+
storageBucket: string;
|
|
106
|
+
messagingSenderId: string;
|
|
107
|
+
appId: string;
|
|
108
|
+
measurementId: string;
|
|
109
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Re-export from @phygitallabs/api-core package
|
|
2
|
+
export type {
|
|
3
|
+
CampaignModel,
|
|
4
|
+
UserCampaignModel,
|
|
5
|
+
MissionWithProgress,
|
|
6
|
+
TaskWithProgress,
|
|
7
|
+
UserCampaignMission,
|
|
8
|
+
Mission,
|
|
9
|
+
MissionProgress,
|
|
10
|
+
TaskProgress,
|
|
11
|
+
} from "@phygitallabs/api-core";
|
|
12
|
+
|
|
13
|
+
export enum TaskType {
|
|
14
|
+
CHECK_IN = "check_in",
|
|
15
|
+
CREATE_MEMORY = "create_memory",
|
|
16
|
+
FILL_FORM = "fill_form",
|
|
17
|
+
SHARE = "share",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Define other types locally since they're not exported from api-core package
|
|
21
|
+
export interface CampaignByProjectIdModel {
|
|
22
|
+
id: string;
|
|
23
|
+
certificate_index?: number;
|
|
24
|
+
completed_checkin_rule?: number;
|
|
25
|
+
completed_device_uids?: string[];
|
|
26
|
+
completed_location_ids?: string[];
|
|
27
|
+
completed_user_ids?: string[];
|
|
28
|
+
description?: string;
|
|
29
|
+
image_url?: string;
|
|
30
|
+
is_custom_checkin_rule?: boolean;
|
|
31
|
+
location_ids?: string[];
|
|
32
|
+
name: string;
|
|
33
|
+
total_completed?: number;
|
|
34
|
+
user_campaign?: any;
|
|
35
|
+
custom_info: Record<string, any>;
|
|
36
|
+
mission: Record<string, any>;
|
|
37
|
+
achievement_id: string;
|
|
38
|
+
start_date: string;
|
|
39
|
+
end_date: string;
|
|
40
|
+
certificate: {
|
|
41
|
+
image: any;
|
|
42
|
+
title: string;
|
|
43
|
+
};
|
|
44
|
+
project_id?: string;
|
|
45
|
+
status: string;
|
|
46
|
+
created_at: number;
|
|
47
|
+
updated_at: number;
|
|
48
|
+
created_by?: string;
|
|
49
|
+
updated_by?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Re-export from @phygitallabs/api-core package
|
|
2
|
+
export type {
|
|
3
|
+
GetOneCampaignRequest,
|
|
4
|
+
GetManyCampaignRequest,
|
|
5
|
+
CreateCampaignRequest,
|
|
6
|
+
CreateCampaignResponse,
|
|
7
|
+
UpdateCampaignRequest,
|
|
8
|
+
UpdateCampaignResponse,
|
|
9
|
+
GetManyCampaignResponse
|
|
10
|
+
} from "@phygitallabs/api-core";
|
|
11
|
+
|
|
12
|
+
// Define other types locally since they're not exported from api-core package
|
|
13
|
+
|
|
14
|
+
export interface UserCheckinLocationRanking {
|
|
15
|
+
user_id: string;
|
|
16
|
+
email: string;
|
|
17
|
+
full_name: string;
|
|
18
|
+
picture: string;
|
|
19
|
+
completed_location_count: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface CampaignLocationSummaryQueryOptions {
|
|
23
|
+
page?: number;
|
|
24
|
+
limit?: number;
|
|
25
|
+
orderBy?: string;
|
|
26
|
+
orderDirection?: 'asc' | 'desc';
|
|
27
|
+
[key: string]: any; // optional: for flexibility if other query options exist
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface CampaignLocationSummary {
|
|
31
|
+
address: {
|
|
32
|
+
custom_map_position: {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
};
|
|
36
|
+
province: string;
|
|
37
|
+
region: string;
|
|
38
|
+
};
|
|
39
|
+
id: string;
|
|
40
|
+
name?: string;
|
|
41
|
+
position: {
|
|
42
|
+
latitude: number;
|
|
43
|
+
longitude: number;
|
|
44
|
+
};
|
|
45
|
+
visit_count?: number;
|
|
46
|
+
}
|