Amba-Agent-App/lib/services/recipientService.ts
2026-01-16 00:22:35 +03:00

249 lines
7.2 KiB
TypeScript

/**
* Recipient Service - Platform-aware recipient management
* Uses Firebase abstraction layer for cross-platform support
*/
import { doc, collection } from '../firebase';
export interface Recipient {
id: string;
uid: string;
fullName: string;
phoneNumber: string;
createdAt: Date;
updatedAt: Date;
isActive: boolean;
}
export class RecipientService {
private static snapshotExists(docSnap: any): boolean {
const existsValue = docSnap?.exists;
if (typeof existsValue === 'function') {
try {
return !!existsValue.call(docSnap);
} catch {
return false;
}
}
return !!existsValue;
}
// Add a new recipient
static async addRecipient(
uid: string,
recipientData: {
fullName: string;
phoneNumber: string;
}
): Promise<{ success: boolean; error?: string; recipientId?: string }> {
try {
// Validate input
if (!recipientData.fullName.trim()) {
return { success: false, error: 'Full name is required' };
}
if (!recipientData.phoneNumber.trim()) {
return { success: false, error: 'Phone number is required' };
}
// Basic phone number validation
const cleanPhone = recipientData.phoneNumber.replace(/[^+\d]/g, '');
if (cleanPhone.length < 7) {
return { success: false, error: 'Please enter a valid phone number' };
}
// Check if recipient already exists
const existingRecipient = await RecipientService.getRecipientByPhone(uid, cleanPhone);
if (existingRecipient) {
return { success: false, error: 'This recipient already exists' };
}
// Generate recipient ID
const recipientId = `recipient_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const newRecipient: Recipient = {
id: recipientId,
uid,
fullName: recipientData.fullName.trim(),
phoneNumber: cleanPhone,
createdAt: new Date(),
updatedAt: new Date(),
isActive: true,
};
// Save to Firestore
const recipientRef = doc('recipients', recipientId);
await recipientRef.set(newRecipient);
return { success: true, recipientId };
} catch (error) {
console.error('Add recipient error:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to add recipient'
};
}
}
// Get all recipients for a user
static async getUserRecipients(uid: string): Promise<Recipient[]> {
try {
const recipientsCollection = collection('recipients');
const querySnapshot = await recipientsCollection
.where('uid', '==', uid)
.where('isActive', '==', true)
.get();
const recipients: Recipient[] = [];
querySnapshot.forEach((docData: any) => {
recipients.push(docData.data() as Recipient);
});
// Sort by creation date (newest first)
recipients.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
return recipients;
} catch (error) {
console.error('Error fetching user recipients:', error);
throw error;
}
}
// Get recipient by phone number for a specific user
static async getRecipientByPhone(uid: string, phoneNumber: string): Promise<Recipient | null> {
try {
const recipientsCollection = collection('recipients');
const querySnapshot = await recipientsCollection
.where('uid', '==', uid)
.where('phoneNumber', '==', phoneNumber)
.where('isActive', '==', true)
.get();
if (querySnapshot.empty) {
return null;
}
return querySnapshot.docs[0].data() as Recipient;
} catch (error) {
console.error('Error fetching recipient by phone:', error);
throw error;
}
}
// Update recipient
static async updateRecipient(
recipientId: string,
updates: {
fullName?: string;
phoneNumber?: string;
}
): Promise<{ success: boolean; error?: string }> {
try {
const recipientRef = doc('recipients', recipientId);
const recipientDoc = await recipientRef.get();
if (!RecipientService.snapshotExists(recipientDoc)) {
return { success: false, error: 'Recipient not found' };
}
const updateData: any = {
updatedAt: new Date(),
};
if (updates.fullName) {
updateData.fullName = updates.fullName.trim();
}
if (updates.phoneNumber) {
const cleanPhone = updates.phoneNumber.replace(/[^+\d]/g, '');
if (cleanPhone.length < 7) {
return { success: false, error: 'Please enter a valid phone number' };
}
updateData.phoneNumber = cleanPhone;
}
await recipientRef.update(updateData);
console.log('Recipient updated successfully');
return { success: true };
} catch (error) {
console.error('Update recipient error:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to update recipient'
};
}
}
// Delete recipient (soft delete)
static async deleteRecipient(recipientId: string): Promise<{ success: boolean; error?: string }> {
try {
const recipientRef = doc('recipients', recipientId);
await recipientRef.update({
isActive: false,
updatedAt: new Date(),
});
console.log('Recipient deleted successfully');
return { success: true };
} catch (error) {
console.error('Delete recipient error:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to delete recipient'
};
}
}
// Hard delete recipient (permanent)
static async permanentDeleteRecipient(recipientId: string): Promise<{ success: boolean; error?: string }> {
try {
const recipientRef = doc('recipients', recipientId);
await recipientRef.delete();
console.log('Recipient permanently deleted');
return { success: true };
} catch (error) {
console.error('Permanent delete recipient error:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to permanently delete recipient'
};
}
}
static async getRecipientById(recipientId: string): Promise<{ success: boolean; recipient?: Recipient; error?: string }> {
try {
const recipientRef = doc('recipients', recipientId);
const docSnap = await recipientRef.get();
if (!RecipientService.snapshotExists(docSnap)) {
return { success: false, error: 'Recipient not found' };
}
const data = docSnap.data() as Recipient | undefined;
if (!data) {
return { success: false, error: 'Recipient data unavailable' };
}
const recipient: Recipient = {
...data,
id: data.id ?? recipientId,
createdAt: (data.createdAt as any)?.toDate?.() ?? data.createdAt ?? new Date(),
updatedAt: (data.updatedAt as any)?.toDate?.() ?? data.updatedAt ?? new Date(),
};
return { success: true, recipient };
} catch (error) {
console.error('Error fetching recipient by id:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to fetch recipient',
};
}
}
}
export default RecipientService;