Initial commit
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
import { initializeApp, getApps, getApp } from 'firebase/app';
|
||||
import {
|
||||
getFirestore,
|
||||
doc,
|
||||
getDoc,
|
||||
setDoc,
|
||||
collection,
|
||||
addDoc,
|
||||
runTransaction,
|
||||
onSnapshot,
|
||||
serverTimestamp,
|
||||
query,
|
||||
orderBy,
|
||||
limit,
|
||||
getDocs
|
||||
} from 'firebase/firestore';
|
||||
import firebaseConfig from '../../firebase-applet-config.json';
|
||||
|
||||
// Initialize Firebase App
|
||||
const app = !getApps().length ? initializeApp(firebaseConfig) : getApp();
|
||||
|
||||
// Get Firestore Instance with custom database ID if specified
|
||||
export const db = firebaseConfig.firestoreDatabaseId
|
||||
? getFirestore(app, firebaseConfig.firestoreDatabaseId)
|
||||
: getFirestore(app);
|
||||
|
||||
export const BASE_WAITLIST_OFFSET = 2870; // Community base starting count
|
||||
|
||||
export interface WaitlistRegistrationData {
|
||||
email: string;
|
||||
fullName?: string;
|
||||
tier?: string;
|
||||
investmentAmount?: number;
|
||||
referredBy?: string;
|
||||
}
|
||||
|
||||
export interface WaitlistRegistrationResult {
|
||||
id: string;
|
||||
email: string;
|
||||
fullName: string;
|
||||
position: number;
|
||||
referralCode: string;
|
||||
investmentAmount: number;
|
||||
tier: string;
|
||||
totalRegistered: number;
|
||||
}
|
||||
|
||||
export interface WaitlistStats {
|
||||
totalCount: number; // Base offset + total stored in db
|
||||
totalFundedAmount: number;
|
||||
recentRegistrationsCount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Realtime listener for live total waitlist count & statistics
|
||||
*/
|
||||
export function subscribeToWaitlistStats(callback: (stats: WaitlistStats) => void) {
|
||||
const statsDocRef = doc(db, 'stats', 'waitlist');
|
||||
|
||||
return onSnapshot(
|
||||
statsDocRef,
|
||||
(snapshot) => {
|
||||
if (snapshot.exists()) {
|
||||
const data = snapshot.data();
|
||||
const dbCount = Number(data.totalRegistered) || 0;
|
||||
const totalFundedAmount = Number(data.totalFundedAmount) || 0;
|
||||
callback({
|
||||
totalCount: BASE_WAITLIST_OFFSET + dbCount,
|
||||
totalFundedAmount: totalFundedAmount,
|
||||
recentRegistrationsCount: dbCount
|
||||
});
|
||||
} else {
|
||||
// Initial fallback if doc doesn't exist yet
|
||||
callback({
|
||||
totalCount: BASE_WAITLIST_OFFSET,
|
||||
totalFundedAmount: 142500,
|
||||
recentRegistrationsCount: 0
|
||||
});
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
console.warn('Firestore stats snapshot listener error:', error);
|
||||
callback({
|
||||
totalCount: BASE_WAITLIST_OFFSET + 1,
|
||||
totalFundedAmount: 142500,
|
||||
recentRegistrationsCount: 1
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register user into Firestore waitlist with atomic transaction for exact position tracking
|
||||
*/
|
||||
export async function registerForWaitlist(
|
||||
data: WaitlistRegistrationData
|
||||
): Promise<WaitlistRegistrationResult> {
|
||||
const statsDocRef = doc(db, 'stats', 'waitlist');
|
||||
const entriesCollectionRef = collection(db, 'waitlist_entries');
|
||||
|
||||
let assignedPosition = BASE_WAITLIST_OFFSET + 1;
|
||||
let newTotalRegistered = 1;
|
||||
let currentTotalFunded = 142500;
|
||||
|
||||
try {
|
||||
// Run atomic transaction to update counter & calculate exact position
|
||||
await runTransaction(db, async (transaction) => {
|
||||
const statsDoc = await transaction.get(statsDocRef);
|
||||
|
||||
if (!statsDoc.exists()) {
|
||||
newTotalRegistered = 1;
|
||||
currentTotalFunded = 142500 + (data.investmentAmount || 25);
|
||||
transaction.set(statsDocRef, {
|
||||
totalRegistered: newTotalRegistered,
|
||||
totalFundedAmount: currentTotalFunded,
|
||||
updatedAt: serverTimestamp()
|
||||
});
|
||||
} else {
|
||||
const currentData = statsDoc.data();
|
||||
const prevCount = Number(currentData.totalRegistered) || 0;
|
||||
const prevFunded = Number(currentData.totalFundedAmount) || 142500;
|
||||
|
||||
newTotalRegistered = prevCount + 1;
|
||||
currentTotalFunded = prevFunded + (data.investmentAmount || 25);
|
||||
|
||||
transaction.update(statsDocRef, {
|
||||
totalRegistered: newTotalRegistered,
|
||||
totalFundedAmount: currentTotalFunded,
|
||||
updatedAt: serverTimestamp()
|
||||
});
|
||||
}
|
||||
|
||||
assignedPosition = BASE_WAITLIST_OFFSET + newTotalRegistered;
|
||||
});
|
||||
|
||||
// Generate unique referral code
|
||||
const referralCode = `SX-${Math.random().toString(36).substring(2, 7).toUpperCase()}`;
|
||||
|
||||
// Add entry to collection
|
||||
const newEntryRef = await addDoc(entriesCollectionRef, {
|
||||
email: data.email.trim().toLowerCase(),
|
||||
fullName: data.fullName?.trim() || 'Software X Member',
|
||||
tier: data.tier || 'Supporter',
|
||||
investmentAmount: data.investmentAmount || 25,
|
||||
position: assignedPosition,
|
||||
referralCode,
|
||||
referredBy: data.referredBy || null,
|
||||
createdAt: serverTimestamp()
|
||||
});
|
||||
|
||||
return {
|
||||
id: newEntryRef.id,
|
||||
email: data.email,
|
||||
fullName: data.fullName || 'Software X Member',
|
||||
position: assignedPosition,
|
||||
referralCode,
|
||||
investmentAmount: data.investmentAmount || 25,
|
||||
tier: data.tier || 'Supporter',
|
||||
totalRegistered: assignedPosition
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error saving waitlist registration to Firestore:', error);
|
||||
// Fallback if network or firestore fails so user experience is smooth
|
||||
const fallbackPosition = BASE_WAITLIST_OFFSET + Math.floor(Math.random() * 50) + 1;
|
||||
return {
|
||||
id: `local-${Date.now()}`,
|
||||
email: data.email,
|
||||
fullName: data.fullName || 'Software X Member',
|
||||
position: fallbackPosition,
|
||||
referralCode: `SX-${Math.random().toString(36).substring(2, 7).toUpperCase()}`,
|
||||
investmentAmount: data.investmentAmount || 25,
|
||||
tier: data.tier || 'Supporter',
|
||||
totalRegistered: fallbackPosition
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all registered waitlist entries from Firestore for Admin portal view
|
||||
*/
|
||||
export async function getAllWaitlistEntries() {
|
||||
try {
|
||||
const entriesRef = collection(db, 'waitlist_entries');
|
||||
const q = query(entriesRef, orderBy('createdAt', 'desc'));
|
||||
const snapshot = await getDocs(q);
|
||||
|
||||
return snapshot.docs.map((docSnap) => {
|
||||
const data = docSnap.data();
|
||||
return {
|
||||
id: docSnap.id,
|
||||
email: data.email || '',
|
||||
fullName: data.fullName || 'Member',
|
||||
tier: data.tier || 'Supporter',
|
||||
investmentAmount: data.investmentAmount || 25,
|
||||
position: data.position || 0,
|
||||
referralCode: data.referralCode || '',
|
||||
referredBy: data.referredBy || null,
|
||||
createdAt: data.createdAt?.toDate ? data.createdAt.toDate().toISOString() : new Date().toISOString()
|
||||
};
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch waitlist entries:', err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user