updates to database schema
Build And Push Image / docker (push) Successful in 2m50s
Details
Build And Push Image / docker (push) Successful in 2m50s
Details
This commit is contained in:
parent
28fa779dae
commit
b308b8272c
|
|
@ -23,7 +23,6 @@
|
||||||
:close-on-content-click="false"
|
:close-on-content-click="false"
|
||||||
location="bottom start"
|
location="bottom start"
|
||||||
:offset="isMobile ? 8 : 4"
|
:offset="isMobile ? 8 : 4"
|
||||||
:max-height="isMobile ? '70vh' : '300px'"
|
|
||||||
min-width="280"
|
min-width="280"
|
||||||
:transition="isMobile ? 'slide-y-transition' : 'fade-transition'"
|
:transition="isMobile ? 'slide-y-transition' : 'fade-transition'"
|
||||||
>
|
>
|
||||||
|
|
@ -161,12 +160,7 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { parsePhoneNumber, AsYouType } from 'libphonenumber-js';
|
import { parsePhoneNumber, AsYouType } from 'libphonenumber-js';
|
||||||
|
import { getPhoneCountriesWithPreferred, searchPhoneCountries, getPhoneCountryByCode, type PhoneCountry } from '~/utils/phone-countries';
|
||||||
interface Country {
|
|
||||||
name: string;
|
|
||||||
iso2: string;
|
|
||||||
dialCode: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
modelValue?: string;
|
modelValue?: string;
|
||||||
|
|
@ -183,8 +177,8 @@ interface Props {
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: 'update:modelValue', value: string): void;
|
(e: 'update:modelValue', value: string): void;
|
||||||
(e: 'country-changed', country: Country): void;
|
(e: 'country-changed', country: PhoneCountry): void;
|
||||||
(e: 'phone-data', data: { number: string; isValid: boolean; country: Country }): void;
|
(e: 'phone-data', data: { number: string; isValid: boolean; country: PhoneCountry }): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
|
@ -199,46 +193,15 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
// Countries data - comprehensive list
|
// Get comprehensive countries list
|
||||||
const countries: Country[] = [
|
const countries = getPhoneCountriesWithPreferred(props.preferredCountries);
|
||||||
{ name: 'Monaco', iso2: 'MC', dialCode: '+377' },
|
|
||||||
{ name: 'France', iso2: 'FR', dialCode: '+33' },
|
|
||||||
{ name: 'United States', iso2: 'US', dialCode: '+1' },
|
|
||||||
{ name: 'Italy', iso2: 'IT', dialCode: '+39' },
|
|
||||||
{ name: 'Switzerland', iso2: 'CH', dialCode: '+41' },
|
|
||||||
{ name: 'United Kingdom', iso2: 'GB', dialCode: '+44' },
|
|
||||||
{ name: 'Germany', iso2: 'DE', dialCode: '+49' },
|
|
||||||
{ name: 'Spain', iso2: 'ES', dialCode: '+34' },
|
|
||||||
{ name: 'Canada', iso2: 'CA', dialCode: '+1' },
|
|
||||||
{ name: 'Australia', iso2: 'AU', dialCode: '+61' },
|
|
||||||
{ name: 'Netherlands', iso2: 'NL', dialCode: '+31' },
|
|
||||||
{ name: 'Belgium', iso2: 'BE', dialCode: '+32' },
|
|
||||||
{ name: 'Portugal', iso2: 'PT', dialCode: '+351' },
|
|
||||||
{ name: 'Austria', iso2: 'AT', dialCode: '+43' },
|
|
||||||
{ name: 'Sweden', iso2: 'SE', dialCode: '+46' },
|
|
||||||
{ name: 'Norway', iso2: 'NO', dialCode: '+47' },
|
|
||||||
{ name: 'Denmark', iso2: 'DK', dialCode: '+45' },
|
|
||||||
{ name: 'Finland', iso2: 'FI', dialCode: '+358' },
|
|
||||||
{ name: 'Ireland', iso2: 'IE', dialCode: '+353' },
|
|
||||||
{ name: 'Luxembourg', iso2: 'LU', dialCode: '+352' },
|
|
||||||
{ name: 'Japan', iso2: 'JP', dialCode: '+81' },
|
|
||||||
{ name: 'South Korea', iso2: 'KR', dialCode: '+82' },
|
|
||||||
{ name: 'Singapore', iso2: 'SG', dialCode: '+65' },
|
|
||||||
{ name: 'Hong Kong', iso2: 'HK', dialCode: '+852' },
|
|
||||||
{ name: 'New Zealand', iso2: 'NZ', dialCode: '+64' },
|
|
||||||
{ name: 'Brazil', iso2: 'BR', dialCode: '+55' },
|
|
||||||
{ name: 'Mexico', iso2: 'MX', dialCode: '+52' },
|
|
||||||
{ name: 'Argentina', iso2: 'AR', dialCode: '+54' },
|
|
||||||
{ name: 'Chile', iso2: 'CL', dialCode: '+56' },
|
|
||||||
{ name: 'South Africa', iso2: 'ZA', dialCode: '+27' }
|
|
||||||
];
|
|
||||||
|
|
||||||
// Reactive state
|
// Reactive state
|
||||||
const dropdownOpen = ref(false);
|
const dropdownOpen = ref(false);
|
||||||
const searchQuery = ref('');
|
const searchQuery = ref('');
|
||||||
const localNumber = ref('');
|
const localNumber = ref('');
|
||||||
const selectedCountry = ref<Country>(
|
const selectedCountry = ref<PhoneCountry>(
|
||||||
countries.find(c => c.iso2 === props.defaultCountry) || countries[0]
|
getPhoneCountryByCode(props.defaultCountry) || countries[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mobile detection
|
// Mobile detection
|
||||||
|
|
@ -262,19 +225,7 @@ onMounted(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredCountries = computed(() => {
|
const filteredCountries = computed(() => {
|
||||||
if (!searchQuery.value) {
|
return searchPhoneCountries(searchQuery.value, props.preferredCountries);
|
||||||
// Show preferred countries first, then alphabetical
|
|
||||||
const preferred = countries.filter(c => isPreferredCountry(c.iso2));
|
|
||||||
const others = countries.filter(c => !isPreferredCountry(c.iso2));
|
|
||||||
return [...preferred, ...others];
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = searchQuery.value.toLowerCase();
|
|
||||||
return countries.filter(country =>
|
|
||||||
country.name.toLowerCase().includes(query) ||
|
|
||||||
country.dialCode.includes(query) ||
|
|
||||||
country.iso2.toLowerCase().includes(query)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
|
|
@ -295,7 +246,7 @@ const toggleDropdown = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectCountry = (country: Country) => {
|
const selectCountry = (country: PhoneCountry) => {
|
||||||
selectedCountry.value = country;
|
selectedCountry.value = country;
|
||||||
dropdownOpen.value = false;
|
dropdownOpen.value = false;
|
||||||
emit('country-changed', country);
|
emit('country-changed', country);
|
||||||
|
|
@ -461,8 +412,11 @@ watch(() => props.modelValue, (newValue) => {
|
||||||
.country-dropdown {
|
.country-dropdown {
|
||||||
min-width: 280px;
|
min-width: 280px;
|
||||||
max-width: 320px;
|
max-width: 320px;
|
||||||
|
max-height: 400px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
|
|
@ -477,7 +431,8 @@ watch(() => props.modelValue, (newValue) => {
|
||||||
|
|
||||||
/* Country List */
|
/* Country List */
|
||||||
.country-list {
|
.country-list {
|
||||||
max-height: 240px;
|
flex: 1;
|
||||||
|
max-height: 300px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: rgba(var(--v-theme-surface), 1);
|
background: rgba(var(--v-theme-surface), 1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,206 @@
|
||||||
|
import { getNocoDbConfiguration } from '~/server/utils/nocodb';
|
||||||
|
import { createSessionManager } from '~/server/utils/session';
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
console.log('[api/admin/membership-status-fix] POST /api/admin/membership-status-fix');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Validate session and require admin privileges
|
||||||
|
const sessionManager = createSessionManager();
|
||||||
|
const cookieHeader = getCookie(event, 'monacousa-session') ? getHeader(event, 'cookie') : undefined;
|
||||||
|
const session = sessionManager.getSession(cookieHeader);
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 401,
|
||||||
|
statusMessage: 'Authentication required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session.user.tier !== 'admin') {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 403,
|
||||||
|
statusMessage: 'Admin privileges required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[api/admin/membership-status-fix] Authorized admin:', session.user.email);
|
||||||
|
|
||||||
|
const body = await readBody(event);
|
||||||
|
const { action, tableId } = body;
|
||||||
|
|
||||||
|
const config = getNocoDbConfiguration();
|
||||||
|
|
||||||
|
if (action === 'check') {
|
||||||
|
return await checkMembershipStatusField(config, tableId);
|
||||||
|
} else if (action === 'fix') {
|
||||||
|
return await fixMembershipStatusField(config, tableId);
|
||||||
|
} else {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
statusMessage: 'Invalid action. Use "check" or "fix"'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('[api/admin/membership-status-fix] Error:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function checkMembershipStatusField(config: any, tableId: string) {
|
||||||
|
console.log('[checkMembershipStatusField] Checking membership status field configuration');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get table schema to check the membership_status field configuration
|
||||||
|
const tableSchema = await $fetch<any>(`${config.url}/api/v2/tables/${tableId}`, {
|
||||||
|
headers: {
|
||||||
|
"xc-token": config.token,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('[checkMembershipStatusField] Table schema fetched');
|
||||||
|
|
||||||
|
// Find the membership_status field
|
||||||
|
const membershipStatusField = tableSchema.columns?.find((col: any) =>
|
||||||
|
col.column_name === 'membership_status' || col.title === 'Membership Status'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!membershipStatusField) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Membership Status field not found in table schema',
|
||||||
|
tableSchema: tableSchema.columns?.map((col: any) => ({
|
||||||
|
name: col.column_name,
|
||||||
|
title: col.title,
|
||||||
|
uidt: col.uidt
|
||||||
|
})) || []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[checkMembershipStatusField] Membership Status field found:', {
|
||||||
|
column_name: membershipStatusField.column_name,
|
||||||
|
title: membershipStatusField.title,
|
||||||
|
uidt: membershipStatusField.uidt,
|
||||||
|
dtxp: membershipStatusField.dtxp
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if it's a Single Select field and what options are available
|
||||||
|
if (membershipStatusField.uidt === 'SingleSelect') {
|
||||||
|
const options = membershipStatusField.colOptions?.options || [];
|
||||||
|
const allowedValues = options.map((opt: any) => opt.title);
|
||||||
|
|
||||||
|
const requiredValues = ['Active', 'Inactive', 'Pending', 'Expired'];
|
||||||
|
const missingValues = requiredValues.filter(val => !allowedValues.includes(val));
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
fieldType: 'SingleSelect',
|
||||||
|
currentOptions: allowedValues,
|
||||||
|
requiredOptions: requiredValues,
|
||||||
|
missingOptions: missingValues,
|
||||||
|
needsFix: missingValues.length > 0,
|
||||||
|
fieldId: membershipStatusField.id,
|
||||||
|
message: missingValues.length > 0
|
||||||
|
? `Missing options: ${missingValues.join(', ')}`
|
||||||
|
: 'All required options are present'
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
fieldType: membershipStatusField.uidt,
|
||||||
|
needsFix: false,
|
||||||
|
message: `Field type is ${membershipStatusField.uidt}, not SingleSelect. This should work fine.`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('[checkMembershipStatusField] Error:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.message || 'Failed to check field configuration'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fixMembershipStatusField(config: any, tableId: string) {
|
||||||
|
console.log('[fixMembershipStatusField] Fixing membership status field configuration');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// First check the current state
|
||||||
|
const checkResult = await checkMembershipStatusField(config, tableId);
|
||||||
|
|
||||||
|
if (!checkResult.success) {
|
||||||
|
throw new Error(checkResult.error || 'Failed to check field configuration');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkResult.needsFix) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'No fix needed - all options are already present',
|
||||||
|
currentOptions: checkResult.currentOptions
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkResult.fieldType !== 'SingleSelect') {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: `Cannot fix field type ${checkResult.fieldType}. Only SingleSelect fields can be updated.`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the field to include all required options
|
||||||
|
const fieldId = checkResult.fieldId;
|
||||||
|
const currentOptions = checkResult.currentOptions || [];
|
||||||
|
const requiredOptions = ['Active', 'Inactive', 'Pending', 'Expired'];
|
||||||
|
|
||||||
|
// Create options array with all required values
|
||||||
|
const optionsToSet = requiredOptions.map((option, index) => ({
|
||||||
|
title: option,
|
||||||
|
color: getStatusColor(option),
|
||||||
|
order: index + 1
|
||||||
|
}));
|
||||||
|
|
||||||
|
console.log('[fixMembershipStatusField] Updating field with options:', optionsToSet);
|
||||||
|
|
||||||
|
// Update the field via NocoDB API
|
||||||
|
const updateResult = await $fetch(`${config.url}/api/v2/tables/${tableId}/columns/${fieldId}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
"xc-token": config.token,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
colOptions: {
|
||||||
|
options: optionsToSet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('[fixMembershipStatusField] Field updated successfully');
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'Membership Status field updated successfully',
|
||||||
|
addedOptions: checkResult.missingOptions,
|
||||||
|
allOptions: requiredOptions
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('[fixMembershipStatusField] Error:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.message || 'Failed to fix field configuration'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatusColor(status: string): string {
|
||||||
|
const colors = {
|
||||||
|
'Active': '#22c55e', // Green
|
||||||
|
'Inactive': '#6b7280', // Gray
|
||||||
|
'Pending': '#f59e0b', // Yellow
|
||||||
|
'Expired': '#ef4444' // Red
|
||||||
|
};
|
||||||
|
return colors[status as keyof typeof colors] || '#6b7280';
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,345 @@
|
||||||
|
import { COUNTRIES } from './countries';
|
||||||
|
|
||||||
|
// Comprehensive mapping of country codes to dial codes
|
||||||
|
// Based on ITU-T E.164 standard
|
||||||
|
export const DIAL_CODES: Record<string, string> = {
|
||||||
|
'AD': '+376', // Andorra
|
||||||
|
'AE': '+971', // United Arab Emirates
|
||||||
|
'AF': '+93', // Afghanistan
|
||||||
|
'AG': '+1', // Antigua and Barbuda
|
||||||
|
'AI': '+1', // Anguilla
|
||||||
|
'AL': '+355', // Albania
|
||||||
|
'AM': '+374', // Armenia
|
||||||
|
'AO': '+244', // Angola
|
||||||
|
'AQ': '+672', // Antarctica
|
||||||
|
'AR': '+54', // Argentina
|
||||||
|
'AS': '+1', // American Samoa
|
||||||
|
'AT': '+43', // Austria
|
||||||
|
'AU': '+61', // Australia
|
||||||
|
'AW': '+297', // Aruba
|
||||||
|
'AX': '+358', // Åland Islands
|
||||||
|
'AZ': '+994', // Azerbaijan
|
||||||
|
'BA': '+387', // Bosnia and Herzegovina
|
||||||
|
'BB': '+1', // Barbados
|
||||||
|
'BD': '+880', // Bangladesh
|
||||||
|
'BE': '+32', // Belgium
|
||||||
|
'BF': '+226', // Burkina Faso
|
||||||
|
'BG': '+359', // Bulgaria
|
||||||
|
'BH': '+973', // Bahrain
|
||||||
|
'BI': '+257', // Burundi
|
||||||
|
'BJ': '+229', // Benin
|
||||||
|
'BL': '+590', // Saint Barthélemy
|
||||||
|
'BM': '+1', // Bermuda
|
||||||
|
'BN': '+673', // Brunei
|
||||||
|
'BO': '+591', // Bolivia
|
||||||
|
'BQ': '+599', // Bonaire, Sint Eustatius and Saba
|
||||||
|
'BR': '+55', // Brazil
|
||||||
|
'BS': '+1', // Bahamas
|
||||||
|
'BT': '+975', // Bhutan
|
||||||
|
'BV': '+47', // Bouvet Island
|
||||||
|
'BW': '+267', // Botswana
|
||||||
|
'BY': '+375', // Belarus
|
||||||
|
'BZ': '+501', // Belize
|
||||||
|
'CA': '+1', // Canada
|
||||||
|
'CC': '+61', // Cocos (Keeling) Islands
|
||||||
|
'CD': '+243', // Congo - Kinshasa
|
||||||
|
'CF': '+236', // Central African Republic
|
||||||
|
'CG': '+242', // Congo - Brazzaville
|
||||||
|
'CH': '+41', // Switzerland
|
||||||
|
'CI': '+225', // Côte d'Ivoire
|
||||||
|
'CK': '+682', // Cook Islands
|
||||||
|
'CL': '+56', // Chile
|
||||||
|
'CM': '+237', // Cameroon
|
||||||
|
'CN': '+86', // China
|
||||||
|
'CO': '+57', // Colombia
|
||||||
|
'CR': '+506', // Costa Rica
|
||||||
|
'CU': '+53', // Cuba
|
||||||
|
'CV': '+238', // Cape Verde
|
||||||
|
'CW': '+599', // Curaçao
|
||||||
|
'CX': '+61', // Christmas Island
|
||||||
|
'CY': '+357', // Cyprus
|
||||||
|
'CZ': '+420', // Czech Republic
|
||||||
|
'DE': '+49', // Germany
|
||||||
|
'DJ': '+253', // Djibouti
|
||||||
|
'DK': '+45', // Denmark
|
||||||
|
'DM': '+1', // Dominica
|
||||||
|
'DO': '+1', // Dominican Republic
|
||||||
|
'DZ': '+213', // Algeria
|
||||||
|
'EC': '+593', // Ecuador
|
||||||
|
'EE': '+372', // Estonia
|
||||||
|
'EG': '+20', // Egypt
|
||||||
|
'EH': '+212', // Western Sahara
|
||||||
|
'ER': '+291', // Eritrea
|
||||||
|
'ES': '+34', // Spain
|
||||||
|
'ET': '+251', // Ethiopia
|
||||||
|
'FI': '+358', // Finland
|
||||||
|
'FJ': '+679', // Fiji
|
||||||
|
'FK': '+500', // Falkland Islands
|
||||||
|
'FM': '+691', // Micronesia
|
||||||
|
'FO': '+298', // Faroe Islands
|
||||||
|
'FR': '+33', // France
|
||||||
|
'GA': '+241', // Gabon
|
||||||
|
'GB': '+44', // United Kingdom
|
||||||
|
'GD': '+1', // Grenada
|
||||||
|
'GE': '+995', // Georgia
|
||||||
|
'GF': '+594', // French Guiana
|
||||||
|
'GG': '+44', // Guernsey
|
||||||
|
'GH': '+233', // Ghana
|
||||||
|
'GI': '+350', // Gibraltar
|
||||||
|
'GL': '+299', // Greenland
|
||||||
|
'GM': '+220', // Gambia
|
||||||
|
'GN': '+224', // Guinea
|
||||||
|
'GP': '+590', // Guadeloupe
|
||||||
|
'GQ': '+240', // Equatorial Guinea
|
||||||
|
'GR': '+30', // Greece
|
||||||
|
'GS': '+500', // South Georgia and the South Sandwich Islands
|
||||||
|
'GT': '+502', // Guatemala
|
||||||
|
'GU': '+1', // Guam
|
||||||
|
'GW': '+245', // Guinea-Bissau
|
||||||
|
'GY': '+592', // Guyana
|
||||||
|
'HK': '+852', // Hong Kong SAR China
|
||||||
|
'HM': '+672', // Heard & McDonald Islands
|
||||||
|
'HN': '+504', // Honduras
|
||||||
|
'HR': '+385', // Croatia
|
||||||
|
'HT': '+509', // Haiti
|
||||||
|
'HU': '+36', // Hungary
|
||||||
|
'ID': '+62', // Indonesia
|
||||||
|
'IE': '+353', // Ireland
|
||||||
|
'IL': '+972', // Israel
|
||||||
|
'IM': '+44', // Isle of Man
|
||||||
|
'IN': '+91', // India
|
||||||
|
'IO': '+246', // British Indian Ocean Territory
|
||||||
|
'IQ': '+964', // Iraq
|
||||||
|
'IR': '+98', // Iran
|
||||||
|
'IS': '+354', // Iceland
|
||||||
|
'IT': '+39', // Italy
|
||||||
|
'JE': '+44', // Jersey
|
||||||
|
'JM': '+1', // Jamaica
|
||||||
|
'JO': '+962', // Jordan
|
||||||
|
'JP': '+81', // Japan
|
||||||
|
'KE': '+254', // Kenya
|
||||||
|
'KG': '+996', // Kyrgyzstan
|
||||||
|
'KH': '+855', // Cambodia
|
||||||
|
'KI': '+686', // Kiribati
|
||||||
|
'KM': '+269', // Comoros
|
||||||
|
'KN': '+1', // Saint Kitts and Nevis
|
||||||
|
'KP': '+850', // North Korea
|
||||||
|
'KR': '+82', // South Korea
|
||||||
|
'KW': '+965', // Kuwait
|
||||||
|
'KY': '+1', // Cayman Islands
|
||||||
|
'KZ': '+7', // Kazakhstan
|
||||||
|
'LA': '+856', // Laos
|
||||||
|
'LB': '+961', // Lebanon
|
||||||
|
'LC': '+1', // Saint Lucia
|
||||||
|
'LI': '+423', // Liechtenstein
|
||||||
|
'LK': '+94', // Sri Lanka
|
||||||
|
'LR': '+231', // Liberia
|
||||||
|
'LS': '+266', // Lesotho
|
||||||
|
'LT': '+370', // Lithuania
|
||||||
|
'LU': '+352', // Luxembourg
|
||||||
|
'LV': '+371', // Latvia
|
||||||
|
'LY': '+218', // Libya
|
||||||
|
'MA': '+212', // Morocco
|
||||||
|
'MC': '+377', // Monaco
|
||||||
|
'MD': '+373', // Moldova
|
||||||
|
'ME': '+382', // Montenegro
|
||||||
|
'MF': '+590', // Saint Martin
|
||||||
|
'MG': '+261', // Madagascar
|
||||||
|
'MH': '+692', // Marshall Islands
|
||||||
|
'MK': '+389', // North Macedonia
|
||||||
|
'ML': '+223', // Mali
|
||||||
|
'MM': '+95', // Myanmar (Burma)
|
||||||
|
'MN': '+976', // Mongolia
|
||||||
|
'MO': '+853', // Macao SAR China
|
||||||
|
'MP': '+1', // Northern Mariana Islands
|
||||||
|
'MQ': '+596', // Martinique
|
||||||
|
'MR': '+222', // Mauritania
|
||||||
|
'MS': '+1', // Montserrat
|
||||||
|
'MT': '+356', // Malta
|
||||||
|
'MU': '+230', // Mauritius
|
||||||
|
'MV': '+960', // Maldives
|
||||||
|
'MW': '+265', // Malawi
|
||||||
|
'MX': '+52', // Mexico
|
||||||
|
'MY': '+60', // Malaysia
|
||||||
|
'MZ': '+258', // Mozambique
|
||||||
|
'NA': '+264', // Namibia
|
||||||
|
'NC': '+687', // New Caledonia
|
||||||
|
'NE': '+227', // Niger
|
||||||
|
'NF': '+672', // Norfolk Island
|
||||||
|
'NG': '+234', // Nigeria
|
||||||
|
'NI': '+505', // Nicaragua
|
||||||
|
'NL': '+31', // Netherlands
|
||||||
|
'NO': '+47', // Norway
|
||||||
|
'NP': '+977', // Nepal
|
||||||
|
'NR': '+674', // Nauru
|
||||||
|
'NU': '+683', // Niue
|
||||||
|
'NZ': '+64', // New Zealand
|
||||||
|
'OM': '+968', // Oman
|
||||||
|
'PA': '+507', // Panama
|
||||||
|
'PE': '+51', // Peru
|
||||||
|
'PF': '+689', // French Polynesia
|
||||||
|
'PG': '+675', // Papua New Guinea
|
||||||
|
'PH': '+63', // Philippines
|
||||||
|
'PK': '+92', // Pakistan
|
||||||
|
'PL': '+48', // Poland
|
||||||
|
'PM': '+508', // Saint Pierre and Miquelon
|
||||||
|
'PN': '+64', // Pitcairn Islands
|
||||||
|
'PR': '+1', // Puerto Rico
|
||||||
|
'PS': '+970', // Palestinian Territories
|
||||||
|
'PT': '+351', // Portugal
|
||||||
|
'PW': '+680', // Palau
|
||||||
|
'PY': '+595', // Paraguay
|
||||||
|
'QA': '+974', // Qatar
|
||||||
|
'RE': '+262', // Réunion
|
||||||
|
'RO': '+40', // Romania
|
||||||
|
'RS': '+381', // Serbia
|
||||||
|
'RU': '+7', // Russia
|
||||||
|
'RW': '+250', // Rwanda
|
||||||
|
'SA': '+966', // Saudi Arabia
|
||||||
|
'SB': '+677', // Solomon Islands
|
||||||
|
'SC': '+248', // Seychelles
|
||||||
|
'SD': '+249', // Sudan
|
||||||
|
'SE': '+46', // Sweden
|
||||||
|
'SG': '+65', // Singapore
|
||||||
|
'SH': '+290', // Saint Helena
|
||||||
|
'SI': '+386', // Slovenia
|
||||||
|
'SJ': '+47', // Svalbard and Jan Mayen
|
||||||
|
'SK': '+421', // Slovakia
|
||||||
|
'SL': '+232', // Sierra Leone
|
||||||
|
'SM': '+378', // San Marino
|
||||||
|
'SN': '+221', // Senegal
|
||||||
|
'SO': '+252', // Somalia
|
||||||
|
'SR': '+597', // Suriname
|
||||||
|
'SS': '+211', // South Sudan
|
||||||
|
'ST': '+239', // São Tomé and Príncipe
|
||||||
|
'SV': '+503', // El Salvador
|
||||||
|
'SX': '+1', // Sint Maarten
|
||||||
|
'SY': '+963', // Syria
|
||||||
|
'SZ': '+268', // Eswatini
|
||||||
|
'TC': '+1', // Turks and Caicos Islands
|
||||||
|
'TD': '+235', // Chad
|
||||||
|
'TF': '+262', // French Southern Territories
|
||||||
|
'TG': '+228', // Togo
|
||||||
|
'TH': '+66', // Thailand
|
||||||
|
'TJ': '+992', // Tajikistan
|
||||||
|
'TK': '+690', // Tokelau
|
||||||
|
'TL': '+670', // Timor-Leste
|
||||||
|
'TM': '+993', // Turkmenistan
|
||||||
|
'TN': '+216', // Tunisia
|
||||||
|
'TO': '+676', // Tonga
|
||||||
|
'TR': '+90', // Turkey
|
||||||
|
'TT': '+1', // Trinidad and Tobago
|
||||||
|
'TV': '+688', // Tuvalu
|
||||||
|
'TW': '+886', // Taiwan
|
||||||
|
'TZ': '+255', // Tanzania
|
||||||
|
'UA': '+380', // Ukraine
|
||||||
|
'UG': '+256', // Uganda
|
||||||
|
'UM': '+1', // U.S. Outlying Islands
|
||||||
|
'US': '+1', // United States
|
||||||
|
'UY': '+598', // Uruguay
|
||||||
|
'UZ': '+998', // Uzbekistan
|
||||||
|
'VA': '+39', // Vatican City
|
||||||
|
'VC': '+1', // Saint Vincent and the Grenadines
|
||||||
|
'VE': '+58', // Venezuela
|
||||||
|
'VG': '+1', // British Virgin Islands
|
||||||
|
'VI': '+1', // U.S. Virgin Islands
|
||||||
|
'VN': '+84', // Vietnam
|
||||||
|
'VU': '+678', // Vanuatu
|
||||||
|
'WF': '+681', // Wallis and Futuna
|
||||||
|
'WS': '+685', // Samoa
|
||||||
|
'XK': '+383', // Kosovo
|
||||||
|
'YE': '+967', // Yemen
|
||||||
|
'YT': '+262', // Mayotte
|
||||||
|
'ZA': '+27', // South Africa
|
||||||
|
'ZM': '+260', // Zambia
|
||||||
|
'ZW': '+263' // Zimbabwe
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interface for phone country data
|
||||||
|
export interface PhoneCountry {
|
||||||
|
name: string;
|
||||||
|
iso2: string;
|
||||||
|
dialCode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all countries with dial codes for phone input
|
||||||
|
*/
|
||||||
|
export const getAllPhoneCountries = (): PhoneCountry[] => {
|
||||||
|
return COUNTRIES.map(country => ({
|
||||||
|
name: country.name,
|
||||||
|
iso2: country.code,
|
||||||
|
dialCode: DIAL_CODES[country.code] || '+1'
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get preferred countries first, then alphabetical
|
||||||
|
*/
|
||||||
|
export const getPhoneCountriesWithPreferred = (preferredCountries: string[] = ['MC', 'FR', 'US', 'IT', 'CH']): PhoneCountry[] => {
|
||||||
|
const allCountries = getAllPhoneCountries();
|
||||||
|
|
||||||
|
// Separate preferred and non-preferred countries
|
||||||
|
const preferred = allCountries.filter(c => preferredCountries.includes(c.iso2));
|
||||||
|
const others = allCountries.filter(c => !preferredCountries.includes(c.iso2));
|
||||||
|
|
||||||
|
// Sort each group alphabetically
|
||||||
|
preferred.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
others.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
return [...preferred, ...others];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find country by dial code
|
||||||
|
*/
|
||||||
|
export const findCountryByDialCode = (dialCode: string): PhoneCountry | null => {
|
||||||
|
const cleanDialCode = dialCode.startsWith('+') ? dialCode : `+${dialCode}`;
|
||||||
|
const countryCode = Object.entries(DIAL_CODES).find(([_, code]) => code === cleanDialCode)?.[0];
|
||||||
|
|
||||||
|
if (countryCode) {
|
||||||
|
const country = COUNTRIES.find(c => c.code === countryCode);
|
||||||
|
if (country) {
|
||||||
|
return {
|
||||||
|
name: country.name,
|
||||||
|
iso2: country.code,
|
||||||
|
dialCode: cleanDialCode
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get country by ISO2 code
|
||||||
|
*/
|
||||||
|
export const getPhoneCountryByCode = (iso2: string): PhoneCountry | null => {
|
||||||
|
const country = COUNTRIES.find(c => c.code === iso2.toUpperCase());
|
||||||
|
if (country) {
|
||||||
|
return {
|
||||||
|
name: country.name,
|
||||||
|
iso2: country.code,
|
||||||
|
dialCode: DIAL_CODES[country.code] || '+1'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search countries for phone input
|
||||||
|
*/
|
||||||
|
export const searchPhoneCountries = (query: string, preferredCountries: string[] = []): PhoneCountry[] => {
|
||||||
|
if (!query) {
|
||||||
|
return getPhoneCountriesWithPreferred(preferredCountries);
|
||||||
|
}
|
||||||
|
|
||||||
|
const lowerQuery = query.toLowerCase();
|
||||||
|
const allCountries = getAllPhoneCountries();
|
||||||
|
|
||||||
|
return allCountries.filter(country =>
|
||||||
|
country.name.toLowerCase().includes(lowerQuery) ||
|
||||||
|
country.dialCode.includes(query) ||
|
||||||
|
country.iso2.toLowerCase().includes(lowerQuery)
|
||||||
|
);
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue