import { getMembers, updateMember } from './nocodb'; import type { Member } from '~/utils/types'; /** * Generates a unique member ID in the format MUSA-YYYY-XXXX * where YYYY is the current year and XXXX is a sequential number * Checks against existing member IDs to ensure uniqueness * @returns Promise - The unique member ID */ export async function generateMemberID(): Promise { console.log('[member-id] Generating new member ID...'); const currentYear = new Date().getFullYear(); let memberID: string; let isUnique = false; let attempts = 0; const maxAttempts = 1000; // Prevent infinite loops // Get all existing member IDs for this year to find the highest number const members = await getMembers(); const memberList = Array.isArray(members) ? members : members?.list || []; // Filter member IDs that match the current year format const yearPrefix = `MUSA-${currentYear}-`; const existingYearIds = memberList .filter((member: Member) => member.member_id?.startsWith(yearPrefix)) .map((member: Member) => { const parts = member.member_id!.split('-'); return parts.length === 3 ? parseInt(parts[2], 10) : 0; }) .filter((num: number) => !isNaN(num)); // Find the highest number used this year const highestNumber = existingYearIds.length > 0 ? Math.max(...existingYearIds) : 0; let nextNumber = highestNumber + 1; while (!isUnique && attempts < maxAttempts) { attempts++; // Format with leading zeros (4 digits) const formattedNumber = String(nextNumber).padStart(4, '0'); memberID = `MUSA-${currentYear}-${formattedNumber}`; console.log(`[member-id] Attempt ${attempts}: Checking uniqueness of ${memberID}`); // Check if ID already exists in database const existingMember = await checkMemberIDExists(memberID); isUnique = !existingMember; if (!isUnique) { console.log(`[member-id] ID ${memberID} already exists, trying next number...`); nextNumber++; } } if (attempts >= maxAttempts) { console.error('[member-id] Failed to generate unique member ID after maximum attempts'); throw new Error('Failed to generate unique member ID after maximum attempts'); } console.log(`[member-id] ✅ Generated unique member ID: ${memberID!} (attempts: ${attempts})`); return memberID!; } /** * Alias for generateMemberID to match the import in other files */ export async function generateUniqueMemberId(): Promise { return generateMemberID(); } /** * Checks if a member ID already exists in the database * @param memberID - The member ID to check * @returns Promise - True if the member ID exists, false otherwise */ export async function checkMemberIDExists(memberID: string): Promise { try { console.log(`[member-id] Checking if member ID exists: ${memberID}`); // Get all members and check for duplicate member_id const members = await getMembers(); const memberList = Array.isArray(members) ? members : members?.list || []; const existingMember = memberList.find((member: Member) => member.member_id === memberID); const exists = !!existingMember; console.log(`[member-id] Member ID ${memberID} exists: ${exists}`); return exists; } catch (error: any) { console.error('[member-id] Error checking member ID existence:', error); // In case of error, assume it doesn't exist to allow generation to continue // The actual creation will fail if there's a real database issue return false; } } /** * Finds all members without a member_id field * Used for migration purposes * @returns Promise - Array of members without member IDs */ export async function findMembersWithoutMemberID(): Promise { try { console.log('[member-id] Finding members without member IDs for migration...'); const members = await getMembers(); const memberList = Array.isArray(members) ? members : members?.list || []; const membersWithoutId = memberList.filter((member: Member) => !member.member_id || member.member_id.trim() === '' ); console.log(`[member-id] Found ${membersWithoutId.length} members without member IDs`); return membersWithoutId; } catch (error: any) { console.error('[member-id] Error finding members without member IDs:', error); throw error; } } /** * Validates a member ID format * @param memberID - The member ID to validate * @returns boolean - True if valid format, false otherwise */ export function isValidMemberIDFormat(memberID: string): boolean { if (!memberID || typeof memberID !== 'string') { return false; } // Check format: MUSA-YYYY-XXXX (year and 4 digits) const memberIDRegex = /^MUSA-\d{4}-\d{4}$/; return memberIDRegex.test(memberID); } /** * Extracts the numeric part from a member ID * @param memberID - The member ID (e.g., "MUSA-123456") * @returns number - The numeric part or null if invalid */ export function extractMemberIDNumber(memberID: string): number | null { if (!isValidMemberIDFormat(memberID)) { return null; } const numericPart = memberID.replace('MUSA-', ''); return parseInt(numericPart, 10); }