@open-mercato/ui 0.8.1-develop.7275.1.f772b944d4 → 0.8.1-develop.7294.1.0ef99fc518
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/dist/backend/DataTable.js +30 -11
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/detail/InlineEditors.js +10 -6
- package/dist/backend/detail/InlineEditors.js.map +2 -2
- package/dist/backend/inputs/PhoneNumberField.js +59 -5
- package/dist/backend/inputs/PhoneNumberField.js.map +2 -2
- package/dist/portal/PortalContext.js +2 -1
- package/dist/portal/PortalContext.js.map +2 -2
- package/dist/portal/hooks/useCustomerAuth.js +3 -4
- package/dist/portal/hooks/useCustomerAuth.js.map +2 -2
- package/package.json +3 -3
- package/src/backend/DataTable.tsx +72 -13
- package/src/backend/__tests__/DataTable.metaHiddenLateColumns.test.tsx +94 -4
- package/src/backend/__tests__/DataTable.saveViewApi.test.tsx +129 -1
- package/src/backend/detail/InlineEditors.tsx +18 -6
- package/src/backend/detail/__tests__/InlineEditors.test.tsx +52 -0
- package/src/backend/inputs/PhoneNumberField.tsx +121 -6
- package/src/backend/inputs/__tests__/PhoneNumberField.test.tsx +92 -1
- package/src/portal/PortalContext.tsx +2 -1
- package/src/portal/__tests__/logoutPageReload.test.tsx +118 -0
- package/src/portal/hooks/useCustomerAuth.ts +3 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/inputs/PhoneNumberField.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { extractPhoneDigits, validatePhoneNumber } from '@open-mercato/shared/lib/phone'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectItemLeading,\n SelectTrigger,\n} from '../../primitives/select'\n\nexport type PhoneDuplicateMatch = {\n id: string\n label: string\n href: string\n}\n\nexport type PhoneCountry = {\n /** ISO 3166-1 alpha-2 code. */\n iso2: string\n /** International dial code (with `+`). */\n dialCode: string\n /** Human-readable country name (English). Override per surface for i18n. */\n label: string\n /** Emoji flag (no asset dependency). */\n flag: string\n}\n\n/**\n * Emoji flag for an ISO 3166-1 alpha-2 code, assembled from the two regional\n * indicator symbols. Deriving it keeps the (large) dictionary free of hand-typed\n * emoji and guarantees the flag always matches the code.\n */\nfunction iso2ToFlagEmoji(iso2: string): string {\n const letters = iso2.toUpperCase().replace(/[^A-Z]/g, '')\n if (letters.length !== 2) return ''\n const base = 0x1f1e6\n return String.fromCodePoint(\n base + letters.charCodeAt(0) - 65,\n base + letters.charCodeAt(1) - 65,\n )\n}\n\n/**\n * Complete static dictionary of geographic countries and territories that have\n * an ISO 3166-1 alpha-2 code and an international calling code, based on the\n * ITU / Wikipedia list of telephone country codes.\n *\n * Rules baked into this data:\n * - Labels are English; flags are derived from the ISO code (`iso2ToFlagEmoji`).\n * - Non-geographic / international service codes (`+800`, `+808`, `+870`,\n * `+881`, `+882`, \u2026) are intentionally excluded.\n * - North American Numbering Plan territories carry their full `+1<NPA>` dial\n * code (e.g. Bahamas `+1242`) so longest-prefix matching resolves them before\n * the generic `+1`. The US and Canada BOTH use a bare `+1` and cannot be told\n * apart from the prefix alone \u2014 auto-detection favours the US.\n * - Several sovereign countries share a calling code with dependent territories\n * (e.g. `+44` UK/Guernsey/Jersey/Isle of Man); `PRIMARY_DIAL_OWNERS` records\n * the country that wins auto-detection for those shared codes.\n */\nconst RAW_PHONE_COUNTRIES: Array<Omit<PhoneCountry, 'flag'>> = [\n { iso2: 'US', dialCode: '+1', label: 'United States' },\n { iso2: 'CA', dialCode: '+1', label: 'Canada' },\n { iso2: 'AG', dialCode: '+1268', label: 'Antigua and Barbuda' },\n { iso2: 'AI', dialCode: '+1264', label: 'Anguilla' },\n { iso2: 'AS', dialCode: '+1684', label: 'American Samoa' },\n { iso2: 'BB', dialCode: '+1246', label: 'Barbados' },\n { iso2: 'BM', dialCode: '+1441', label: 'Bermuda' },\n { iso2: 'BS', dialCode: '+1242', label: 'Bahamas' },\n { iso2: 'DM', dialCode: '+1767', label: 'Dominica' },\n { iso2: 'DO', dialCode: '+1809', label: 'Dominican Republic' },\n { iso2: 'GD', dialCode: '+1473', label: 'Grenada' },\n { iso2: 'GU', dialCode: '+1671', label: 'Guam' },\n { iso2: 'JM', dialCode: '+1876', label: 'Jamaica' },\n { iso2: 'KN', dialCode: '+1869', label: 'Saint Kitts and Nevis' },\n { iso2: 'KY', dialCode: '+1345', label: 'Cayman Islands' },\n { iso2: 'LC', dialCode: '+1758', label: 'Saint Lucia' },\n { iso2: 'MP', dialCode: '+1670', label: 'Northern Mariana Islands' },\n { iso2: 'MS', dialCode: '+1664', label: 'Montserrat' },\n { iso2: 'PR', dialCode: '+1787', label: 'Puerto Rico' },\n { iso2: 'SX', dialCode: '+1721', label: 'Sint Maarten' },\n { iso2: 'TC', dialCode: '+1649', label: 'Turks and Caicos Islands' },\n { iso2: 'TT', dialCode: '+1868', label: 'Trinidad and Tobago' },\n { iso2: 'VC', dialCode: '+1784', label: 'Saint Vincent and the Grenadines' },\n { iso2: 'VG', dialCode: '+1284', label: 'British Virgin Islands' },\n { iso2: 'VI', dialCode: '+1340', label: 'U.S. Virgin Islands' },\n { iso2: 'EG', dialCode: '+20', label: 'Egypt' },\n { iso2: 'SS', dialCode: '+211', label: 'South Sudan' },\n { iso2: 'MA', dialCode: '+212', label: 'Morocco' },\n { iso2: 'EH', dialCode: '+212', label: 'Western Sahara' },\n { iso2: 'DZ', dialCode: '+213', label: 'Algeria' },\n { iso2: 'TN', dialCode: '+216', label: 'Tunisia' },\n { iso2: 'LY', dialCode: '+218', label: 'Libya' },\n { iso2: 'GM', dialCode: '+220', label: 'Gambia' },\n { iso2: 'SN', dialCode: '+221', label: 'Senegal' },\n { iso2: 'MR', dialCode: '+222', label: 'Mauritania' },\n { iso2: 'ML', dialCode: '+223', label: 'Mali' },\n { iso2: 'GN', dialCode: '+224', label: 'Guinea' },\n { iso2: 'CI', dialCode: '+225', label: \"C\u00F4te d'Ivoire\" },\n { iso2: 'BF', dialCode: '+226', label: 'Burkina Faso' },\n { iso2: 'NE', dialCode: '+227', label: 'Niger' },\n { iso2: 'TG', dialCode: '+228', label: 'Togo' },\n { iso2: 'BJ', dialCode: '+229', label: 'Benin' },\n { iso2: 'MU', dialCode: '+230', label: 'Mauritius' },\n { iso2: 'LR', dialCode: '+231', label: 'Liberia' },\n { iso2: 'SL', dialCode: '+232', label: 'Sierra Leone' },\n { iso2: 'GH', dialCode: '+233', label: 'Ghana' },\n { iso2: 'NG', dialCode: '+234', label: 'Nigeria' },\n { iso2: 'TD', dialCode: '+235', label: 'Chad' },\n { iso2: 'CF', dialCode: '+236', label: 'Central African Republic' },\n { iso2: 'CM', dialCode: '+237', label: 'Cameroon' },\n { iso2: 'CV', dialCode: '+238', label: 'Cape Verde' },\n { iso2: 'ST', dialCode: '+239', label: 'S\u00E3o Tom\u00E9 and Pr\u00EDncipe' },\n { iso2: 'GQ', dialCode: '+240', label: 'Equatorial Guinea' },\n { iso2: 'GA', dialCode: '+241', label: 'Gabon' },\n { iso2: 'CG', dialCode: '+242', label: 'Republic of the Congo' },\n { iso2: 'CD', dialCode: '+243', label: 'DR Congo' },\n { iso2: 'AO', dialCode: '+244', label: 'Angola' },\n { iso2: 'GW', dialCode: '+245', label: 'Guinea-Bissau' },\n { iso2: 'IO', dialCode: '+246', label: 'British Indian Ocean Territory' },\n { iso2: 'SC', dialCode: '+248', label: 'Seychelles' },\n { iso2: 'SD', dialCode: '+249', label: 'Sudan' },\n { iso2: 'RW', dialCode: '+250', label: 'Rwanda' },\n { iso2: 'ET', dialCode: '+251', label: 'Ethiopia' },\n { iso2: 'SO', dialCode: '+252', label: 'Somalia' },\n { iso2: 'DJ', dialCode: '+253', label: 'Djibouti' },\n { iso2: 'KE', dialCode: '+254', label: 'Kenya' },\n { iso2: 'TZ', dialCode: '+255', label: 'Tanzania' },\n { iso2: 'UG', dialCode: '+256', label: 'Uganda' },\n { iso2: 'BI', dialCode: '+257', label: 'Burundi' },\n { iso2: 'MZ', dialCode: '+258', label: 'Mozambique' },\n { iso2: 'ZM', dialCode: '+260', label: 'Zambia' },\n { iso2: 'MG', dialCode: '+261', label: 'Madagascar' },\n { iso2: 'RE', dialCode: '+262', label: 'R\u00E9union' },\n { iso2: 'YT', dialCode: '+262', label: 'Mayotte' },\n { iso2: 'ZW', dialCode: '+263', label: 'Zimbabwe' },\n { iso2: 'NA', dialCode: '+264', label: 'Namibia' },\n { iso2: 'MW', dialCode: '+265', label: 'Malawi' },\n { iso2: 'LS', dialCode: '+266', label: 'Lesotho' },\n { iso2: 'BW', dialCode: '+267', label: 'Botswana' },\n { iso2: 'SZ', dialCode: '+268', label: 'Eswatini' },\n { iso2: 'KM', dialCode: '+269', label: 'Comoros' },\n { iso2: 'ZA', dialCode: '+27', label: 'South Africa' },\n { iso2: 'SH', dialCode: '+290', label: 'Saint Helena' },\n { iso2: 'ER', dialCode: '+291', label: 'Eritrea' },\n { iso2: 'AW', dialCode: '+297', label: 'Aruba' },\n { iso2: 'FO', dialCode: '+298', label: 'Faroe Islands' },\n { iso2: 'GL', dialCode: '+299', label: 'Greenland' },\n { iso2: 'GR', dialCode: '+30', label: 'Greece' },\n { iso2: 'NL', dialCode: '+31', label: 'Netherlands' },\n { iso2: 'BE', dialCode: '+32', label: 'Belgium' },\n { iso2: 'FR', dialCode: '+33', label: 'France' },\n { iso2: 'ES', dialCode: '+34', label: 'Spain' },\n { iso2: 'GI', dialCode: '+350', label: 'Gibraltar' },\n { iso2: 'PT', dialCode: '+351', label: 'Portugal' },\n { iso2: 'LU', dialCode: '+352', label: 'Luxembourg' },\n { iso2: 'IE', dialCode: '+353', label: 'Ireland' },\n { iso2: 'IS', dialCode: '+354', label: 'Iceland' },\n { iso2: 'AL', dialCode: '+355', label: 'Albania' },\n { iso2: 'MT', dialCode: '+356', label: 'Malta' },\n { iso2: 'CY', dialCode: '+357', label: 'Cyprus' },\n { iso2: 'FI', dialCode: '+358', label: 'Finland' },\n { iso2: 'AX', dialCode: '+358', label: '\u00C5land Islands' },\n { iso2: 'BG', dialCode: '+359', label: 'Bulgaria' },\n { iso2: 'HU', dialCode: '+36', label: 'Hungary' },\n { iso2: 'LT', dialCode: '+370', label: 'Lithuania' },\n { iso2: 'LV', dialCode: '+371', label: 'Latvia' },\n { iso2: 'EE', dialCode: '+372', label: 'Estonia' },\n { iso2: 'MD', dialCode: '+373', label: 'Moldova' },\n { iso2: 'AM', dialCode: '+374', label: 'Armenia' },\n { iso2: 'BY', dialCode: '+375', label: 'Belarus' },\n { iso2: 'AD', dialCode: '+376', label: 'Andorra' },\n { iso2: 'MC', dialCode: '+377', label: 'Monaco' },\n { iso2: 'SM', dialCode: '+378', label: 'San Marino' },\n { iso2: 'VA', dialCode: '+379', label: 'Vatican City' },\n { iso2: 'UA', dialCode: '+380', label: 'Ukraine' },\n { iso2: 'RS', dialCode: '+381', label: 'Serbia' },\n { iso2: 'ME', dialCode: '+382', label: 'Montenegro' },\n { iso2: 'XK', dialCode: '+383', label: 'Kosovo' },\n { iso2: 'HR', dialCode: '+385', label: 'Croatia' },\n { iso2: 'SI', dialCode: '+386', label: 'Slovenia' },\n { iso2: 'BA', dialCode: '+387', label: 'Bosnia and Herzegovina' },\n { iso2: 'MK', dialCode: '+389', label: 'North Macedonia' },\n { iso2: 'IT', dialCode: '+39', label: 'Italy' },\n { iso2: 'RO', dialCode: '+40', label: 'Romania' },\n { iso2: 'CH', dialCode: '+41', label: 'Switzerland' },\n { iso2: 'CZ', dialCode: '+420', label: 'Czechia' },\n { iso2: 'SK', dialCode: '+421', label: 'Slovakia' },\n { iso2: 'LI', dialCode: '+423', label: 'Liechtenstein' },\n { iso2: 'AT', dialCode: '+43', label: 'Austria' },\n { iso2: 'GB', dialCode: '+44', label: 'United Kingdom' },\n { iso2: 'GG', dialCode: '+44', label: 'Guernsey' },\n { iso2: 'JE', dialCode: '+44', label: 'Jersey' },\n { iso2: 'IM', dialCode: '+44', label: 'Isle of Man' },\n { iso2: 'DK', dialCode: '+45', label: 'Denmark' },\n { iso2: 'SE', dialCode: '+46', label: 'Sweden' },\n { iso2: 'NO', dialCode: '+47', label: 'Norway' },\n { iso2: 'SJ', dialCode: '+47', label: 'Svalbard and Jan Mayen' },\n { iso2: 'PL', dialCode: '+48', label: 'Poland' },\n { iso2: 'DE', dialCode: '+49', label: 'Germany' },\n { iso2: 'FK', dialCode: '+500', label: 'Falkland Islands' },\n { iso2: 'BZ', dialCode: '+501', label: 'Belize' },\n { iso2: 'GT', dialCode: '+502', label: 'Guatemala' },\n { iso2: 'SV', dialCode: '+503', label: 'El Salvador' },\n { iso2: 'HN', dialCode: '+504', label: 'Honduras' },\n { iso2: 'NI', dialCode: '+505', label: 'Nicaragua' },\n { iso2: 'CR', dialCode: '+506', label: 'Costa Rica' },\n { iso2: 'PA', dialCode: '+507', label: 'Panama' },\n { iso2: 'PM', dialCode: '+508', label: 'Saint Pierre and Miquelon' },\n { iso2: 'HT', dialCode: '+509', label: 'Haiti' },\n { iso2: 'PE', dialCode: '+51', label: 'Peru' },\n { iso2: 'MX', dialCode: '+52', label: 'Mexico' },\n { iso2: 'CU', dialCode: '+53', label: 'Cuba' },\n { iso2: 'AR', dialCode: '+54', label: 'Argentina' },\n { iso2: 'BR', dialCode: '+55', label: 'Brazil' },\n { iso2: 'CL', dialCode: '+56', label: 'Chile' },\n { iso2: 'CO', dialCode: '+57', label: 'Colombia' },\n { iso2: 'VE', dialCode: '+58', label: 'Venezuela' },\n { iso2: 'GP', dialCode: '+590', label: 'Guadeloupe' },\n { iso2: 'BL', dialCode: '+590', label: 'Saint Barth\u00E9lemy' },\n { iso2: 'MF', dialCode: '+590', label: 'Saint Martin' },\n { iso2: 'BO', dialCode: '+591', label: 'Bolivia' },\n { iso2: 'GY', dialCode: '+592', label: 'Guyana' },\n { iso2: 'EC', dialCode: '+593', label: 'Ecuador' },\n { iso2: 'GF', dialCode: '+594', label: 'French Guiana' },\n { iso2: 'PY', dialCode: '+595', label: 'Paraguay' },\n { iso2: 'MQ', dialCode: '+596', label: 'Martinique' },\n { iso2: 'SR', dialCode: '+597', label: 'Suriname' },\n { iso2: 'UY', dialCode: '+598', label: 'Uruguay' },\n { iso2: 'CW', dialCode: '+599', label: 'Cura\u00E7ao' },\n { iso2: 'BQ', dialCode: '+599', label: 'Caribbean Netherlands' },\n { iso2: 'MY', dialCode: '+60', label: 'Malaysia' },\n { iso2: 'AU', dialCode: '+61', label: 'Australia' },\n { iso2: 'CX', dialCode: '+61', label: 'Christmas Island' },\n { iso2: 'CC', dialCode: '+61', label: 'Cocos (Keeling) Islands' },\n { iso2: 'ID', dialCode: '+62', label: 'Indonesia' },\n { iso2: 'PH', dialCode: '+63', label: 'Philippines' },\n { iso2: 'NZ', dialCode: '+64', label: 'New Zealand' },\n { iso2: 'SG', dialCode: '+65', label: 'Singapore' },\n { iso2: 'TH', dialCode: '+66', label: 'Thailand' },\n { iso2: 'TL', dialCode: '+670', label: 'Timor-Leste' },\n { iso2: 'NF', dialCode: '+672', label: 'Norfolk Island' },\n { iso2: 'BN', dialCode: '+673', label: 'Brunei' },\n { iso2: 'NR', dialCode: '+674', label: 'Nauru' },\n { iso2: 'PG', dialCode: '+675', label: 'Papua New Guinea' },\n { iso2: 'TO', dialCode: '+676', label: 'Tonga' },\n { iso2: 'SB', dialCode: '+677', label: 'Solomon Islands' },\n { iso2: 'VU', dialCode: '+678', label: 'Vanuatu' },\n { iso2: 'FJ', dialCode: '+679', label: 'Fiji' },\n { iso2: 'PW', dialCode: '+680', label: 'Palau' },\n { iso2: 'WF', dialCode: '+681', label: 'Wallis and Futuna' },\n { iso2: 'CK', dialCode: '+682', label: 'Cook Islands' },\n { iso2: 'NU', dialCode: '+683', label: 'Niue' },\n { iso2: 'WS', dialCode: '+685', label: 'Samoa' },\n { iso2: 'KI', dialCode: '+686', label: 'Kiribati' },\n { iso2: 'NC', dialCode: '+687', label: 'New Caledonia' },\n { iso2: 'TV', dialCode: '+688', label: 'Tuvalu' },\n { iso2: 'PF', dialCode: '+689', label: 'French Polynesia' },\n { iso2: 'TK', dialCode: '+690', label: 'Tokelau' },\n { iso2: 'FM', dialCode: '+691', label: 'Micronesia' },\n { iso2: 'MH', dialCode: '+692', label: 'Marshall Islands' },\n { iso2: 'RU', dialCode: '+7', label: 'Russia' },\n { iso2: 'KZ', dialCode: '+7', label: 'Kazakhstan' },\n { iso2: 'JP', dialCode: '+81', label: 'Japan' },\n { iso2: 'KR', dialCode: '+82', label: 'South Korea' },\n { iso2: 'VN', dialCode: '+84', label: 'Vietnam' },\n { iso2: 'KP', dialCode: '+850', label: 'North Korea' },\n { iso2: 'HK', dialCode: '+852', label: 'Hong Kong' },\n { iso2: 'MO', dialCode: '+853', label: 'Macau' },\n { iso2: 'KH', dialCode: '+855', label: 'Cambodia' },\n { iso2: 'LA', dialCode: '+856', label: 'Laos' },\n { iso2: 'CN', dialCode: '+86', label: 'China' },\n { iso2: 'BD', dialCode: '+880', label: 'Bangladesh' },\n { iso2: 'TW', dialCode: '+886', label: 'Taiwan' },\n { iso2: 'TR', dialCode: '+90', label: 'Turkey' },\n { iso2: 'IN', dialCode: '+91', label: 'India' },\n { iso2: 'PK', dialCode: '+92', label: 'Pakistan' },\n { iso2: 'AF', dialCode: '+93', label: 'Afghanistan' },\n { iso2: 'LK', dialCode: '+94', label: 'Sri Lanka' },\n { iso2: 'MM', dialCode: '+95', label: 'Myanmar' },\n { iso2: 'MV', dialCode: '+960', label: 'Maldives' },\n { iso2: 'LB', dialCode: '+961', label: 'Lebanon' },\n { iso2: 'JO', dialCode: '+962', label: 'Jordan' },\n { iso2: 'SY', dialCode: '+963', label: 'Syria' },\n { iso2: 'IQ', dialCode: '+964', label: 'Iraq' },\n { iso2: 'KW', dialCode: '+965', label: 'Kuwait' },\n { iso2: 'SA', dialCode: '+966', label: 'Saudi Arabia' },\n { iso2: 'YE', dialCode: '+967', label: 'Yemen' },\n { iso2: 'OM', dialCode: '+968', label: 'Oman' },\n { iso2: 'PS', dialCode: '+970', label: 'Palestine' },\n { iso2: 'AE', dialCode: '+971', label: 'United Arab Emirates' },\n { iso2: 'IL', dialCode: '+972', label: 'Israel' },\n { iso2: 'BH', dialCode: '+973', label: 'Bahrain' },\n { iso2: 'QA', dialCode: '+974', label: 'Qatar' },\n { iso2: 'BT', dialCode: '+975', label: 'Bhutan' },\n { iso2: 'MN', dialCode: '+976', label: 'Mongolia' },\n { iso2: 'NP', dialCode: '+977', label: 'Nepal' },\n { iso2: 'TJ', dialCode: '+992', label: 'Tajikistan' },\n { iso2: 'TM', dialCode: '+993', label: 'Turkmenistan' },\n { iso2: 'AZ', dialCode: '+994', label: 'Azerbaijan' },\n { iso2: 'GE', dialCode: '+995', label: 'Georgia' },\n { iso2: 'KG', dialCode: '+996', label: 'Kyrgyzstan' },\n { iso2: 'UZ', dialCode: '+998', label: 'Uzbekistan' },\n { iso2: 'IR', dialCode: '+98', label: 'Iran' },\n]\n\nexport const PHONE_COUNTRIES: PhoneCountry[] = RAW_PHONE_COUNTRIES\n .map((country) => ({ ...country, flag: iso2ToFlagEmoji(country.iso2) }))\n .sort((a, b) => a.label.localeCompare(b.label, 'en', { sensitivity: 'base' }))\n\n/**\n * Sovereign/primary country that wins auto-detection for a calling code shared\n * by several territories. US vs. Canada is the one irreducible ambiguity \u2014 both\n * are `+1` \u2014 and it resolves to the US.\n */\nconst PRIMARY_DIAL_OWNERS: Record<string, string> = {\n '+1': 'US',\n '+7': 'RU',\n '+44': 'GB',\n '+47': 'NO',\n '+61': 'AU',\n '+212': 'MA',\n '+262': 'RE',\n '+358': 'FI',\n '+590': 'GP',\n '+599': 'CW',\n}\n\nconst DEFAULT_COUNTRY =\n PHONE_COUNTRIES.find((country) => country.iso2 === 'US') ?? PHONE_COUNTRIES[0]\n\n// Match longer prefixes first (e.g. `+1242` before `+1`); for equal-length codes\n// shared by several territories, prefer the sovereign in `PRIMARY_DIAL_OWNERS`.\nconst COUNTRIES_BY_DIAL_LENGTH = [...PHONE_COUNTRIES].sort((a, b) => {\n if (b.dialCode.length !== a.dialCode.length) return b.dialCode.length - a.dialCode.length\n const aPrimary = PRIMARY_DIAL_OWNERS[a.dialCode] === a.iso2 ? 0 : 1\n const bPrimary = PRIMARY_DIAL_OWNERS[b.dialCode] === b.iso2 ? 0 : 1\n return aPrimary - bPrimary\n})\n\nfunction findCountryByIso(iso2: string): PhoneCountry | undefined {\n return PHONE_COUNTRIES.find((c) => c.iso2 === iso2)\n}\n\nfunction parseCountryFromValue(value: string | null | undefined): PhoneCountry | null {\n if (!value) return null\n const trimmed = value.trim()\n if (!trimmed.startsWith('+')) return null\n for (const country of COUNTRIES_BY_DIAL_LENGTH) {\n if (trimmed.startsWith(country.dialCode)) return country\n }\n return null\n}\n\nfunction extractLocalNumber(value: string | null | undefined, dialCode: string): string {\n if (!value) return ''\n const trimmed = value.trim()\n if (!trimmed.startsWith(dialCode)) return trimmed\n return trimmed.slice(dialCode.length).trim()\n}\n\nfunction composeValue(country: PhoneCountry, localNumber: string): string {\n const trimmed = localNumber.trim()\n if (!trimmed) return ''\n if (trimmed.startsWith('+')) return trimmed\n return `${country.dialCode} ${trimmed}`\n}\n\nexport type PhoneNumberFieldProps = {\n id?: string\n value?: string | null\n onValueChange: (next: string | undefined) => void\n onDigitsChange?: (digits: string | null) => void\n externalError?: string | null\n disabled?: boolean\n autoFocus?: boolean\n ariaLabel?: string\n ariaDescribedBy?: string\n placeholder?: string\n minDigits?: number\n checkingLabel?: string\n duplicateLabel?: (match: PhoneDuplicateMatch) => string\n duplicateLinkLabel?: string\n invalidLabel?: string\n onDuplicateLookup?: (normalizedValue: string) => Promise<PhoneDuplicateMatch | null>\n /** Override the static country list (e.g. limit to specific markets). */\n countries?: PhoneCountry[]\n /** Initial country shown when `value` is empty / unparseable. Defaults to US. */\n defaultCountryIso2?: string\n size?: 32 | 36 | 40\n countryLabel?: string\n renderCountryIcon?: (country: PhoneCountry) => React.ReactNode\n}\n\nconst DEFAULT_MIN_DIGITS = 6\nconst DEFAULT_PLACEHOLDER = '(555) 000-0000'\n\nexport function PhoneNumberField({\n id,\n value,\n onValueChange,\n onDigitsChange,\n externalError,\n disabled = false,\n autoFocus,\n ariaLabel,\n ariaDescribedBy,\n placeholder,\n minDigits = DEFAULT_MIN_DIGITS,\n checkingLabel,\n duplicateLabel,\n duplicateLinkLabel,\n invalidLabel,\n onDuplicateLookup,\n countries: countriesProp,\n defaultCountryIso2,\n size,\n countryLabel,\n renderCountryIcon,\n}: PhoneNumberFieldProps) {\n const t = useT()\n const resolvedInvalidLabel = invalidLabel ?? t(\n 'ui.inputs.phoneNumberField.invalid',\n 'Enter a valid phone number with country code (e.g. +1 212 555 1234)'\n )\n const resolvedCheckingLabel = checkingLabel ?? t(\n 'ui.inputs.phoneNumberField.checking',\n 'Checking for duplicates\u2026'\n )\n const resolvedDuplicateLinkLabel = duplicateLinkLabel ?? t(\n 'ui.inputs.phoneNumberField.duplicateLink',\n 'Open record'\n )\n const resolvedPlaceholder = placeholder ?? DEFAULT_PLACEHOLDER\n const countries = countriesProp ?? PHONE_COUNTRIES\n const fallbackCountry = React.useMemo(\n () => (defaultCountryIso2 && findCountryByIso(defaultCountryIso2)) || DEFAULT_COUNTRY,\n [defaultCountryIso2],\n )\n\n const initialCountry = React.useMemo(\n () => parseCountryFromValue(value) ?? fallbackCountry,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n )\n const [country, setCountry] = React.useState<PhoneCountry>(initialCountry)\n const [localNumber, setLocalNumber] = React.useState<string>(() =>\n extractLocalNumber(value, initialCountry.dialCode),\n )\n const [duplicate, setDuplicate] = React.useState<PhoneDuplicateMatch | null>(null)\n const [checking, setChecking] = React.useState(false)\n const [validationHint, setValidationHint] = React.useState<string | null>(null)\n const [focused, setFocused] = React.useState(false)\n const userEditingRef = React.useRef(false)\n\n const externalFieldError = externalError && externalError.trim().length > 0 ? externalError : null\n const errorMessage = externalFieldError ?? validationHint\n const errorId = errorMessage && id ? `${id}-error` : undefined\n const describedBy = [ariaDescribedBy, errorId].filter((part): part is string => Boolean(part)).join(' ') || undefined\n const composedValue = composeValue(country, localNumber)\n\n // Sync local state when `value` updates externally and the user is not editing.\n React.useEffect(() => {\n if (userEditingRef.current) return\n if (value == null || value === '') {\n setLocalNumber('')\n onDigitsChange?.(null)\n return\n }\n const parsed = parseCountryFromValue(value)\n if (parsed) setCountry(parsed)\n const next = extractLocalNumber(value, parsed?.dialCode ?? country.dialCode)\n setLocalNumber(next)\n onDigitsChange?.(extractPhoneDigits(String(value)) || null)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value])\n\n // Debounced duplicate lookup.\n React.useEffect(() => {\n if (!onDuplicateLookup || disabled) {\n setDuplicate(null)\n setChecking(false)\n return\n }\n const digits = extractPhoneDigits(composedValue)\n if (!digits || digits.length < minDigits) {\n setDuplicate(null)\n setChecking(false)\n return\n }\n\n let cancelled = false\n setChecking(true)\n const handle = window.setTimeout(async () => {\n try {\n const match = await onDuplicateLookup(digits)\n if (!cancelled) setDuplicate(match)\n } catch {\n if (!cancelled) setDuplicate(null)\n } finally {\n if (!cancelled) setChecking(false)\n }\n }, 350)\n\n return () => {\n cancelled = true\n window.clearTimeout(handle)\n }\n }, [composedValue, disabled, minDigits, onDuplicateLookup])\n\n const emit = React.useCallback(\n (nextCountry: PhoneCountry, nextLocal: string) => {\n const composed = composeValue(nextCountry, nextLocal)\n onValueChange(composed.length ? composed : undefined)\n onDigitsChange?.(extractPhoneDigits(composed) || null)\n },\n [onDigitsChange, onValueChange],\n )\n\n const handleCountryChange = React.useCallback(\n (iso2: string) => {\n const next = findCountryByIso(iso2)\n if (!next) return\n userEditingRef.current = true\n setCountry(next)\n setValidationHint(null)\n emit(next, localNumber)\n },\n [emit, localNumber],\n )\n\n const handleLocalChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n const next = event.target.value\n userEditingRef.current = true\n setLocalNumber(next)\n setValidationHint(null)\n emit(country, next)\n },\n [country, emit],\n )\n\n const handleBlur = React.useCallback(() => {\n userEditingRef.current = false\n setFocused(false)\n const trimmed = localNumber.trim()\n if (!trimmed) {\n setLocalNumber('')\n setValidationHint(null)\n onValueChange(undefined)\n onDigitsChange?.(null)\n return\n }\n const composed = composeValue(country, trimmed)\n const result = validatePhoneNumber(composed)\n if (result.valid) {\n const normalizedLocal = extractLocalNumber(result.normalized ?? composed, country.dialCode)\n setLocalNumber(normalizedLocal)\n setValidationHint(null)\n onValueChange(result.normalized || undefined)\n onDigitsChange?.(result.digits || null)\n } else {\n setValidationHint(resolvedInvalidLabel)\n onValueChange(composed)\n onDigitsChange?.(result.digits || null)\n }\n }, [country, localNumber, onDigitsChange, onValueChange, resolvedInvalidLabel])\n\n const containerErrorBorder = errorMessage ? 'border-status-error-icon' : 'border-input'\n const containerFocusBorder = errorMessage\n ? 'border-status-error-icon shadow-focus'\n : 'border-brand-violet shadow-focus'\n\n return (\n <div className=\"space-y-2\">\n <div\n className={cn(\n 'flex items-stretch w-full rounded-md border bg-background shadow-xs transition-colors',\n size === 32 && 'h-8',\n size === 36 && 'h-9',\n size === 40 && 'h-10 rounded-lg',\n disabled\n ? 'bg-bg-disabled border-border-disabled cursor-not-allowed'\n : focused\n ? containerFocusBorder\n : `${containerErrorBorder} hover:border-foreground/30`,\n )}\n aria-invalid={errorMessage ? 'true' : undefined}\n data-slot=\"phone-input-wrapper\"\n >\n <Select value={country.iso2} onValueChange={handleCountryChange} disabled={disabled}>\n <SelectTrigger\n aria-label={countryLabel ?? (ariaLabel ? `${ariaLabel} country` : t('ui.inputs.phoneNumberField.country', 'Country code'))}\n size={size}\n className={cn(\n 'h-auto w-auto shrink-0 gap-1.5 rounded-none rounded-l-md border-0 bg-transparent px-2.5 py-2 shadow-none',\n 'hover:bg-muted/40 focus:bg-muted/40 focus-visible:shadow-none focus-visible:border-0',\n 'disabled:bg-transparent disabled:hover:bg-transparent',\n size && 'h-full gap-2 rounded-l-none py-0 pl-2.5 pr-2 [&>svg]:size-5',\n size === 32 && 'pl-1.5 pr-1',\n )}\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n >\n <span className={cn('text-base leading-none', size && 'inline-flex size-5 shrink-0 items-center justify-center text-xl [&_img]:size-full')} aria-hidden=\"true\">{renderCountryIcon ? renderCountryIcon(country) : country.flag}</span>\n <span className={cn(\"text-sm text-foreground tabular-nums\", disabled && \"text-text-disabled\")}>{country.dialCode}</span>\n </SelectTrigger>\n <SelectContent align=\"start\">\n {countries.map((c) => (\n <SelectItem key={`${c.iso2}-${c.dialCode}`} value={c.iso2}>\n <SelectItemLeading>\n <span className=\"text-base leading-none\">{c.flag}</span>\n </SelectItemLeading>\n <span className=\"flex-1 truncate\">{c.label}</span>\n <span className=\"ml-2 text-xs text-muted-foreground tabular-nums\">{c.dialCode}</span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <div aria-hidden=\"true\" className=\"w-px self-stretch bg-input\" />\n <input\n type=\"tel\"\n inputMode=\"tel\"\n autoComplete=\"tel-national\"\n value={localNumber}\n onChange={handleLocalChange}\n onBlur={handleBlur}\n onFocus={() => setFocused(true)}\n placeholder={resolvedPlaceholder}\n autoFocus={autoFocus}\n disabled={disabled}\n id={id}\n aria-label={ariaLabel}\n aria-describedby={describedBy}\n aria-invalid={errorMessage ? 'true' : undefined}\n data-crud-focus-target=\"\"\n className={cn(\n 'flex-1 min-w-0 bg-transparent px-3 py-2 text-sm leading-5 outline-none',\n 'placeholder:text-muted-foreground',\n 'disabled:cursor-not-allowed disabled:text-text-disabled disabled:placeholder:text-text-disabled',\n size && 'h-full py-0 pr-2.5',\n size === 32 && 'pl-2 pr-1.5',\n )}\n />\n </div>\n {errorMessage ? (\n <p id={errorId} role=\"alert\" className=\"text-xs text-status-error-text\">{errorMessage}</p>\n ) : null}\n {!disabled && duplicate && duplicateLabel ? (\n <p className=\"text-xs text-status-warning-text\">\n {duplicateLabel(duplicate)}{' '}\n <a className=\"font-medium text-brand-violet underline underline-offset-2\" href={duplicate.href}>\n {resolvedDuplicateLinkLabel}\n </a>\n </p>\n ) : null}\n {!disabled && !duplicate && checking ? (\n <p className=\"text-xs text-muted-foreground\">{resolvedCheckingLabel}</p>\n ) : null}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useOptionalLocale, useT, type TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { extractPhoneDigits, validatePhoneNumber } from '@open-mercato/shared/lib/phone'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectItemLeading,\n SelectTrigger,\n} from '../../primitives/select'\n\nexport type PhoneDuplicateMatch = {\n id: string\n label: string\n href: string\n}\n\nexport type PhoneCountry = {\n /** ISO 3166-1 alpha-2 code. */\n iso2: string\n /** International dial code (with `+`). */\n dialCode: string\n /**\n * Human-readable country name (English). Used as the last-resort fallback\n * when neither a locale file nor the platform's region data can name the\n * country in the active locale.\n */\n label: string\n /** Emoji flag (no asset dependency). */\n flag: string\n}\n\n/**\n * Emoji flag for an ISO 3166-1 alpha-2 code, assembled from the two regional\n * indicator symbols. Deriving it keeps the (large) dictionary free of hand-typed\n * emoji and guarantees the flag always matches the code.\n */\nfunction iso2ToFlagEmoji(iso2: string): string {\n const letters = iso2.toUpperCase().replace(/[^A-Z]/g, '')\n if (letters.length !== 2) return ''\n const base = 0x1f1e6\n return String.fromCodePoint(\n base + letters.charCodeAt(0) - 65,\n base + letters.charCodeAt(1) - 65,\n )\n}\n\n/**\n * Complete static dictionary of geographic countries and territories that have\n * an ISO 3166-1 alpha-2 code and an international calling code, based on the\n * ITU / Wikipedia list of telephone country codes.\n *\n * Rules baked into this data:\n * - Labels are English; flags are derived from the ISO code (`iso2ToFlagEmoji`).\n * - Non-geographic / international service codes (`+800`, `+808`, `+870`,\n * `+881`, `+882`, \u2026) are intentionally excluded.\n * - North American Numbering Plan territories carry their full `+1<NPA>` dial\n * code (e.g. Bahamas `+1242`) so longest-prefix matching resolves them before\n * the generic `+1`. The US and Canada BOTH use a bare `+1` and cannot be told\n * apart from the prefix alone \u2014 auto-detection favours the US.\n * - Several sovereign countries share a calling code with dependent territories\n * (e.g. `+44` UK/Guernsey/Jersey/Isle of Man); `PRIMARY_DIAL_OWNERS` records\n * the country that wins auto-detection for those shared codes.\n */\nconst RAW_PHONE_COUNTRIES: Array<Omit<PhoneCountry, 'flag'>> = [\n { iso2: 'US', dialCode: '+1', label: 'United States' },\n { iso2: 'CA', dialCode: '+1', label: 'Canada' },\n { iso2: 'AG', dialCode: '+1268', label: 'Antigua and Barbuda' },\n { iso2: 'AI', dialCode: '+1264', label: 'Anguilla' },\n { iso2: 'AS', dialCode: '+1684', label: 'American Samoa' },\n { iso2: 'BB', dialCode: '+1246', label: 'Barbados' },\n { iso2: 'BM', dialCode: '+1441', label: 'Bermuda' },\n { iso2: 'BS', dialCode: '+1242', label: 'Bahamas' },\n { iso2: 'DM', dialCode: '+1767', label: 'Dominica' },\n { iso2: 'DO', dialCode: '+1809', label: 'Dominican Republic' },\n { iso2: 'GD', dialCode: '+1473', label: 'Grenada' },\n { iso2: 'GU', dialCode: '+1671', label: 'Guam' },\n { iso2: 'JM', dialCode: '+1876', label: 'Jamaica' },\n { iso2: 'KN', dialCode: '+1869', label: 'Saint Kitts and Nevis' },\n { iso2: 'KY', dialCode: '+1345', label: 'Cayman Islands' },\n { iso2: 'LC', dialCode: '+1758', label: 'Saint Lucia' },\n { iso2: 'MP', dialCode: '+1670', label: 'Northern Mariana Islands' },\n { iso2: 'MS', dialCode: '+1664', label: 'Montserrat' },\n { iso2: 'PR', dialCode: '+1787', label: 'Puerto Rico' },\n { iso2: 'SX', dialCode: '+1721', label: 'Sint Maarten' },\n { iso2: 'TC', dialCode: '+1649', label: 'Turks and Caicos Islands' },\n { iso2: 'TT', dialCode: '+1868', label: 'Trinidad and Tobago' },\n { iso2: 'VC', dialCode: '+1784', label: 'Saint Vincent and the Grenadines' },\n { iso2: 'VG', dialCode: '+1284', label: 'British Virgin Islands' },\n { iso2: 'VI', dialCode: '+1340', label: 'U.S. Virgin Islands' },\n { iso2: 'EG', dialCode: '+20', label: 'Egypt' },\n { iso2: 'SS', dialCode: '+211', label: 'South Sudan' },\n { iso2: 'MA', dialCode: '+212', label: 'Morocco' },\n { iso2: 'EH', dialCode: '+212', label: 'Western Sahara' },\n { iso2: 'DZ', dialCode: '+213', label: 'Algeria' },\n { iso2: 'TN', dialCode: '+216', label: 'Tunisia' },\n { iso2: 'LY', dialCode: '+218', label: 'Libya' },\n { iso2: 'GM', dialCode: '+220', label: 'Gambia' },\n { iso2: 'SN', dialCode: '+221', label: 'Senegal' },\n { iso2: 'MR', dialCode: '+222', label: 'Mauritania' },\n { iso2: 'ML', dialCode: '+223', label: 'Mali' },\n { iso2: 'GN', dialCode: '+224', label: 'Guinea' },\n { iso2: 'CI', dialCode: '+225', label: \"C\u00F4te d'Ivoire\" },\n { iso2: 'BF', dialCode: '+226', label: 'Burkina Faso' },\n { iso2: 'NE', dialCode: '+227', label: 'Niger' },\n { iso2: 'TG', dialCode: '+228', label: 'Togo' },\n { iso2: 'BJ', dialCode: '+229', label: 'Benin' },\n { iso2: 'MU', dialCode: '+230', label: 'Mauritius' },\n { iso2: 'LR', dialCode: '+231', label: 'Liberia' },\n { iso2: 'SL', dialCode: '+232', label: 'Sierra Leone' },\n { iso2: 'GH', dialCode: '+233', label: 'Ghana' },\n { iso2: 'NG', dialCode: '+234', label: 'Nigeria' },\n { iso2: 'TD', dialCode: '+235', label: 'Chad' },\n { iso2: 'CF', dialCode: '+236', label: 'Central African Republic' },\n { iso2: 'CM', dialCode: '+237', label: 'Cameroon' },\n { iso2: 'CV', dialCode: '+238', label: 'Cape Verde' },\n { iso2: 'ST', dialCode: '+239', label: 'S\u00E3o Tom\u00E9 and Pr\u00EDncipe' },\n { iso2: 'GQ', dialCode: '+240', label: 'Equatorial Guinea' },\n { iso2: 'GA', dialCode: '+241', label: 'Gabon' },\n { iso2: 'CG', dialCode: '+242', label: 'Republic of the Congo' },\n { iso2: 'CD', dialCode: '+243', label: 'DR Congo' },\n { iso2: 'AO', dialCode: '+244', label: 'Angola' },\n { iso2: 'GW', dialCode: '+245', label: 'Guinea-Bissau' },\n { iso2: 'IO', dialCode: '+246', label: 'British Indian Ocean Territory' },\n { iso2: 'SC', dialCode: '+248', label: 'Seychelles' },\n { iso2: 'SD', dialCode: '+249', label: 'Sudan' },\n { iso2: 'RW', dialCode: '+250', label: 'Rwanda' },\n { iso2: 'ET', dialCode: '+251', label: 'Ethiopia' },\n { iso2: 'SO', dialCode: '+252', label: 'Somalia' },\n { iso2: 'DJ', dialCode: '+253', label: 'Djibouti' },\n { iso2: 'KE', dialCode: '+254', label: 'Kenya' },\n { iso2: 'TZ', dialCode: '+255', label: 'Tanzania' },\n { iso2: 'UG', dialCode: '+256', label: 'Uganda' },\n { iso2: 'BI', dialCode: '+257', label: 'Burundi' },\n { iso2: 'MZ', dialCode: '+258', label: 'Mozambique' },\n { iso2: 'ZM', dialCode: '+260', label: 'Zambia' },\n { iso2: 'MG', dialCode: '+261', label: 'Madagascar' },\n { iso2: 'RE', dialCode: '+262', label: 'R\u00E9union' },\n { iso2: 'YT', dialCode: '+262', label: 'Mayotte' },\n { iso2: 'ZW', dialCode: '+263', label: 'Zimbabwe' },\n { iso2: 'NA', dialCode: '+264', label: 'Namibia' },\n { iso2: 'MW', dialCode: '+265', label: 'Malawi' },\n { iso2: 'LS', dialCode: '+266', label: 'Lesotho' },\n { iso2: 'BW', dialCode: '+267', label: 'Botswana' },\n { iso2: 'SZ', dialCode: '+268', label: 'Eswatini' },\n { iso2: 'KM', dialCode: '+269', label: 'Comoros' },\n { iso2: 'ZA', dialCode: '+27', label: 'South Africa' },\n { iso2: 'SH', dialCode: '+290', label: 'Saint Helena' },\n { iso2: 'ER', dialCode: '+291', label: 'Eritrea' },\n { iso2: 'AW', dialCode: '+297', label: 'Aruba' },\n { iso2: 'FO', dialCode: '+298', label: 'Faroe Islands' },\n { iso2: 'GL', dialCode: '+299', label: 'Greenland' },\n { iso2: 'GR', dialCode: '+30', label: 'Greece' },\n { iso2: 'NL', dialCode: '+31', label: 'Netherlands' },\n { iso2: 'BE', dialCode: '+32', label: 'Belgium' },\n { iso2: 'FR', dialCode: '+33', label: 'France' },\n { iso2: 'ES', dialCode: '+34', label: 'Spain' },\n { iso2: 'GI', dialCode: '+350', label: 'Gibraltar' },\n { iso2: 'PT', dialCode: '+351', label: 'Portugal' },\n { iso2: 'LU', dialCode: '+352', label: 'Luxembourg' },\n { iso2: 'IE', dialCode: '+353', label: 'Ireland' },\n { iso2: 'IS', dialCode: '+354', label: 'Iceland' },\n { iso2: 'AL', dialCode: '+355', label: 'Albania' },\n { iso2: 'MT', dialCode: '+356', label: 'Malta' },\n { iso2: 'CY', dialCode: '+357', label: 'Cyprus' },\n { iso2: 'FI', dialCode: '+358', label: 'Finland' },\n { iso2: 'AX', dialCode: '+358', label: '\u00C5land Islands' },\n { iso2: 'BG', dialCode: '+359', label: 'Bulgaria' },\n { iso2: 'HU', dialCode: '+36', label: 'Hungary' },\n { iso2: 'LT', dialCode: '+370', label: 'Lithuania' },\n { iso2: 'LV', dialCode: '+371', label: 'Latvia' },\n { iso2: 'EE', dialCode: '+372', label: 'Estonia' },\n { iso2: 'MD', dialCode: '+373', label: 'Moldova' },\n { iso2: 'AM', dialCode: '+374', label: 'Armenia' },\n { iso2: 'BY', dialCode: '+375', label: 'Belarus' },\n { iso2: 'AD', dialCode: '+376', label: 'Andorra' },\n { iso2: 'MC', dialCode: '+377', label: 'Monaco' },\n { iso2: 'SM', dialCode: '+378', label: 'San Marino' },\n { iso2: 'VA', dialCode: '+379', label: 'Vatican City' },\n { iso2: 'UA', dialCode: '+380', label: 'Ukraine' },\n { iso2: 'RS', dialCode: '+381', label: 'Serbia' },\n { iso2: 'ME', dialCode: '+382', label: 'Montenegro' },\n { iso2: 'XK', dialCode: '+383', label: 'Kosovo' },\n { iso2: 'HR', dialCode: '+385', label: 'Croatia' },\n { iso2: 'SI', dialCode: '+386', label: 'Slovenia' },\n { iso2: 'BA', dialCode: '+387', label: 'Bosnia and Herzegovina' },\n { iso2: 'MK', dialCode: '+389', label: 'North Macedonia' },\n { iso2: 'IT', dialCode: '+39', label: 'Italy' },\n { iso2: 'RO', dialCode: '+40', label: 'Romania' },\n { iso2: 'CH', dialCode: '+41', label: 'Switzerland' },\n { iso2: 'CZ', dialCode: '+420', label: 'Czechia' },\n { iso2: 'SK', dialCode: '+421', label: 'Slovakia' },\n { iso2: 'LI', dialCode: '+423', label: 'Liechtenstein' },\n { iso2: 'AT', dialCode: '+43', label: 'Austria' },\n { iso2: 'GB', dialCode: '+44', label: 'United Kingdom' },\n { iso2: 'GG', dialCode: '+44', label: 'Guernsey' },\n { iso2: 'JE', dialCode: '+44', label: 'Jersey' },\n { iso2: 'IM', dialCode: '+44', label: 'Isle of Man' },\n { iso2: 'DK', dialCode: '+45', label: 'Denmark' },\n { iso2: 'SE', dialCode: '+46', label: 'Sweden' },\n { iso2: 'NO', dialCode: '+47', label: 'Norway' },\n { iso2: 'SJ', dialCode: '+47', label: 'Svalbard and Jan Mayen' },\n { iso2: 'PL', dialCode: '+48', label: 'Poland' },\n { iso2: 'DE', dialCode: '+49', label: 'Germany' },\n { iso2: 'FK', dialCode: '+500', label: 'Falkland Islands' },\n { iso2: 'BZ', dialCode: '+501', label: 'Belize' },\n { iso2: 'GT', dialCode: '+502', label: 'Guatemala' },\n { iso2: 'SV', dialCode: '+503', label: 'El Salvador' },\n { iso2: 'HN', dialCode: '+504', label: 'Honduras' },\n { iso2: 'NI', dialCode: '+505', label: 'Nicaragua' },\n { iso2: 'CR', dialCode: '+506', label: 'Costa Rica' },\n { iso2: 'PA', dialCode: '+507', label: 'Panama' },\n { iso2: 'PM', dialCode: '+508', label: 'Saint Pierre and Miquelon' },\n { iso2: 'HT', dialCode: '+509', label: 'Haiti' },\n { iso2: 'PE', dialCode: '+51', label: 'Peru' },\n { iso2: 'MX', dialCode: '+52', label: 'Mexico' },\n { iso2: 'CU', dialCode: '+53', label: 'Cuba' },\n { iso2: 'AR', dialCode: '+54', label: 'Argentina' },\n { iso2: 'BR', dialCode: '+55', label: 'Brazil' },\n { iso2: 'CL', dialCode: '+56', label: 'Chile' },\n { iso2: 'CO', dialCode: '+57', label: 'Colombia' },\n { iso2: 'VE', dialCode: '+58', label: 'Venezuela' },\n { iso2: 'GP', dialCode: '+590', label: 'Guadeloupe' },\n { iso2: 'BL', dialCode: '+590', label: 'Saint Barth\u00E9lemy' },\n { iso2: 'MF', dialCode: '+590', label: 'Saint Martin' },\n { iso2: 'BO', dialCode: '+591', label: 'Bolivia' },\n { iso2: 'GY', dialCode: '+592', label: 'Guyana' },\n { iso2: 'EC', dialCode: '+593', label: 'Ecuador' },\n { iso2: 'GF', dialCode: '+594', label: 'French Guiana' },\n { iso2: 'PY', dialCode: '+595', label: 'Paraguay' },\n { iso2: 'MQ', dialCode: '+596', label: 'Martinique' },\n { iso2: 'SR', dialCode: '+597', label: 'Suriname' },\n { iso2: 'UY', dialCode: '+598', label: 'Uruguay' },\n { iso2: 'CW', dialCode: '+599', label: 'Cura\u00E7ao' },\n { iso2: 'BQ', dialCode: '+599', label: 'Caribbean Netherlands' },\n { iso2: 'MY', dialCode: '+60', label: 'Malaysia' },\n { iso2: 'AU', dialCode: '+61', label: 'Australia' },\n { iso2: 'CX', dialCode: '+61', label: 'Christmas Island' },\n { iso2: 'CC', dialCode: '+61', label: 'Cocos (Keeling) Islands' },\n { iso2: 'ID', dialCode: '+62', label: 'Indonesia' },\n { iso2: 'PH', dialCode: '+63', label: 'Philippines' },\n { iso2: 'NZ', dialCode: '+64', label: 'New Zealand' },\n { iso2: 'SG', dialCode: '+65', label: 'Singapore' },\n { iso2: 'TH', dialCode: '+66', label: 'Thailand' },\n { iso2: 'TL', dialCode: '+670', label: 'Timor-Leste' },\n { iso2: 'NF', dialCode: '+672', label: 'Norfolk Island' },\n { iso2: 'BN', dialCode: '+673', label: 'Brunei' },\n { iso2: 'NR', dialCode: '+674', label: 'Nauru' },\n { iso2: 'PG', dialCode: '+675', label: 'Papua New Guinea' },\n { iso2: 'TO', dialCode: '+676', label: 'Tonga' },\n { iso2: 'SB', dialCode: '+677', label: 'Solomon Islands' },\n { iso2: 'VU', dialCode: '+678', label: 'Vanuatu' },\n { iso2: 'FJ', dialCode: '+679', label: 'Fiji' },\n { iso2: 'PW', dialCode: '+680', label: 'Palau' },\n { iso2: 'WF', dialCode: '+681', label: 'Wallis and Futuna' },\n { iso2: 'CK', dialCode: '+682', label: 'Cook Islands' },\n { iso2: 'NU', dialCode: '+683', label: 'Niue' },\n { iso2: 'WS', dialCode: '+685', label: 'Samoa' },\n { iso2: 'KI', dialCode: '+686', label: 'Kiribati' },\n { iso2: 'NC', dialCode: '+687', label: 'New Caledonia' },\n { iso2: 'TV', dialCode: '+688', label: 'Tuvalu' },\n { iso2: 'PF', dialCode: '+689', label: 'French Polynesia' },\n { iso2: 'TK', dialCode: '+690', label: 'Tokelau' },\n { iso2: 'FM', dialCode: '+691', label: 'Micronesia' },\n { iso2: 'MH', dialCode: '+692', label: 'Marshall Islands' },\n { iso2: 'RU', dialCode: '+7', label: 'Russia' },\n { iso2: 'KZ', dialCode: '+7', label: 'Kazakhstan' },\n { iso2: 'JP', dialCode: '+81', label: 'Japan' },\n { iso2: 'KR', dialCode: '+82', label: 'South Korea' },\n { iso2: 'VN', dialCode: '+84', label: 'Vietnam' },\n { iso2: 'KP', dialCode: '+850', label: 'North Korea' },\n { iso2: 'HK', dialCode: '+852', label: 'Hong Kong' },\n { iso2: 'MO', dialCode: '+853', label: 'Macau' },\n { iso2: 'KH', dialCode: '+855', label: 'Cambodia' },\n { iso2: 'LA', dialCode: '+856', label: 'Laos' },\n { iso2: 'CN', dialCode: '+86', label: 'China' },\n { iso2: 'BD', dialCode: '+880', label: 'Bangladesh' },\n { iso2: 'TW', dialCode: '+886', label: 'Taiwan' },\n { iso2: 'TR', dialCode: '+90', label: 'Turkey' },\n { iso2: 'IN', dialCode: '+91', label: 'India' },\n { iso2: 'PK', dialCode: '+92', label: 'Pakistan' },\n { iso2: 'AF', dialCode: '+93', label: 'Afghanistan' },\n { iso2: 'LK', dialCode: '+94', label: 'Sri Lanka' },\n { iso2: 'MM', dialCode: '+95', label: 'Myanmar' },\n { iso2: 'MV', dialCode: '+960', label: 'Maldives' },\n { iso2: 'LB', dialCode: '+961', label: 'Lebanon' },\n { iso2: 'JO', dialCode: '+962', label: 'Jordan' },\n { iso2: 'SY', dialCode: '+963', label: 'Syria' },\n { iso2: 'IQ', dialCode: '+964', label: 'Iraq' },\n { iso2: 'KW', dialCode: '+965', label: 'Kuwait' },\n { iso2: 'SA', dialCode: '+966', label: 'Saudi Arabia' },\n { iso2: 'YE', dialCode: '+967', label: 'Yemen' },\n { iso2: 'OM', dialCode: '+968', label: 'Oman' },\n { iso2: 'PS', dialCode: '+970', label: 'Palestine' },\n { iso2: 'AE', dialCode: '+971', label: 'United Arab Emirates' },\n { iso2: 'IL', dialCode: '+972', label: 'Israel' },\n { iso2: 'BH', dialCode: '+973', label: 'Bahrain' },\n { iso2: 'QA', dialCode: '+974', label: 'Qatar' },\n { iso2: 'BT', dialCode: '+975', label: 'Bhutan' },\n { iso2: 'MN', dialCode: '+976', label: 'Mongolia' },\n { iso2: 'NP', dialCode: '+977', label: 'Nepal' },\n { iso2: 'TJ', dialCode: '+992', label: 'Tajikistan' },\n { iso2: 'TM', dialCode: '+993', label: 'Turkmenistan' },\n { iso2: 'AZ', dialCode: '+994', label: 'Azerbaijan' },\n { iso2: 'GE', dialCode: '+995', label: 'Georgia' },\n { iso2: 'KG', dialCode: '+996', label: 'Kyrgyzstan' },\n { iso2: 'UZ', dialCode: '+998', label: 'Uzbekistan' },\n { iso2: 'IR', dialCode: '+98', label: 'Iran' },\n]\n\nexport const PHONE_COUNTRIES: PhoneCountry[] = RAW_PHONE_COUNTRIES\n .map((country) => ({ ...country, flag: iso2ToFlagEmoji(country.iso2) }))\n .sort((a, b) => a.label.localeCompare(b.label, 'en', { sensitivity: 'base' }))\n\n/**\n * Translation key holding a country's display name, e.g.\n * `ui.inputs.phoneNumberField.countryName.PL`. Locale files only need entries\n * for the countries whose platform-provided name a deployment wants to\n * change. This is a distinct namespace from\n * `ui.inputs.phoneNumberField.country` (the picker's trigger label) so a\n * nested locale file can hold both.\n */\nexport function phoneCountryLabelKey(iso2: string): string {\n return `ui.inputs.phoneNumberField.countryName.${iso2.toUpperCase()}`\n}\n\nconst regionDisplayNamesByLocale = new Map<string, Intl.DisplayNames | null>()\n\nfunction getRegionDisplayNames(locale: string): Intl.DisplayNames | null {\n const cached = regionDisplayNamesByLocale.get(locale)\n if (cached !== undefined) return cached\n let displayNames: Intl.DisplayNames | null = null\n try {\n displayNames = new Intl.DisplayNames([locale], { type: 'region' })\n } catch {\n displayNames = null\n }\n regionDisplayNamesByLocale.set(locale, displayNames)\n return displayNames\n}\n\n/**\n * Country name in `locale` from the platform's own region data, or `null` when\n * the runtime cannot name that region (unknown code, trimmed ICU data).\n */\nfunction regionDisplayName(iso2: string, locale: string | undefined): string | null {\n if (!locale) return null\n const code = iso2.toUpperCase()\n if (!/^[A-Z]{2}$/.test(code)) return null\n const displayNames = getRegionDisplayNames(locale)\n if (!displayNames) return null\n try {\n const name = displayNames.of(code)\n return name && name !== code ? name : null\n } catch {\n return null\n }\n}\n\nfunction isEnglishLocale(locale: string | undefined): boolean {\n return !locale || locale.toLowerCase().split(/[-_]/)[0] === 'en'\n}\n\n/**\n * Display name for a built-in country in the active locale. A locale-file entry\n * wins so a deployment can correct or override any name; otherwise the\n * platform's region data localizes it; the English label is the last resort.\n *\n * English deliberately skips the region data: the dictionary already holds\n * curated English names, and CLDR spells several of them differently\n * (\"Antigua & Barbuda\" for \"Antigua and Barbuda\"), so consulting it would\n * silently rewrite existing English copy for no gain.\n */\nexport function resolvePhoneCountryLabel(\n country: PhoneCountry,\n t: TranslateFn,\n locale: string | undefined,\n): string {\n const translated = t(phoneCountryLabelKey(country.iso2), '')\n if (translated) return translated\n if (isEnglishLocale(locale)) return country.label\n return regionDisplayName(country.iso2, locale) ?? country.label\n}\n\nexport type PhoneCountryOption = {\n country: PhoneCountry\n label: string\n}\n\n/**\n * Locale safe to hand to `localeCompare`. A tag `Intl` cannot construct would\n * throw there just as it does in `getRegionDisplayNames`, so both fall back to\n * English together.\n */\nfunction resolveCollationLocale(locale: string | undefined): string {\n if (!locale) return 'en'\n return getRegionDisplayNames(locale) ? locale : 'en'\n}\n\n/**\n * Options rendered in the country dropdown. A caller-supplied list is\n * authoritative \u2014 its labels and its order are preserved verbatim \u2014 while the\n * built-in dictionary is localized and re-sorted for the active locale, since\n * its module-level ordering is alphabetical by the English name.\n */\nexport function buildPhoneCountryOptions(\n countries: PhoneCountry[] | undefined,\n t: TranslateFn,\n locale: string | undefined,\n): PhoneCountryOption[] {\n if (countries) return countries.map((country) => ({ country, label: country.label }))\n const collationLocale = resolveCollationLocale(locale)\n return PHONE_COUNTRIES\n .map((country) => ({ country, label: resolvePhoneCountryLabel(country, t, locale) }))\n .sort((a, b) => a.label.localeCompare(b.label, collationLocale, { sensitivity: 'base' }))\n}\n\n/**\n * Sovereign/primary country that wins auto-detection for a calling code shared\n * by several territories. US vs. Canada is the one irreducible ambiguity \u2014 both\n * are `+1` \u2014 and it resolves to the US.\n */\nconst PRIMARY_DIAL_OWNERS: Record<string, string> = {\n '+1': 'US',\n '+7': 'RU',\n '+44': 'GB',\n '+47': 'NO',\n '+61': 'AU',\n '+212': 'MA',\n '+262': 'RE',\n '+358': 'FI',\n '+590': 'GP',\n '+599': 'CW',\n}\n\nconst DEFAULT_COUNTRY =\n PHONE_COUNTRIES.find((country) => country.iso2 === 'US') ?? PHONE_COUNTRIES[0]\n\n// Match longer prefixes first (e.g. `+1242` before `+1`); for equal-length codes\n// shared by several territories, prefer the sovereign in `PRIMARY_DIAL_OWNERS`.\nconst COUNTRIES_BY_DIAL_LENGTH = [...PHONE_COUNTRIES].sort((a, b) => {\n if (b.dialCode.length !== a.dialCode.length) return b.dialCode.length - a.dialCode.length\n const aPrimary = PRIMARY_DIAL_OWNERS[a.dialCode] === a.iso2 ? 0 : 1\n const bPrimary = PRIMARY_DIAL_OWNERS[b.dialCode] === b.iso2 ? 0 : 1\n return aPrimary - bPrimary\n})\n\nfunction findCountryByIso(iso2: string): PhoneCountry | undefined {\n return PHONE_COUNTRIES.find((c) => c.iso2 === iso2)\n}\n\nfunction parseCountryFromValue(value: string | null | undefined): PhoneCountry | null {\n if (!value) return null\n const trimmed = value.trim()\n if (!trimmed.startsWith('+')) return null\n for (const country of COUNTRIES_BY_DIAL_LENGTH) {\n if (trimmed.startsWith(country.dialCode)) return country\n }\n return null\n}\n\nfunction extractLocalNumber(value: string | null | undefined, dialCode: string): string {\n if (!value) return ''\n const trimmed = value.trim()\n if (!trimmed.startsWith(dialCode)) return trimmed\n return trimmed.slice(dialCode.length).trim()\n}\n\nfunction composeValue(country: PhoneCountry, localNumber: string): string {\n const trimmed = localNumber.trim()\n if (!trimmed) return ''\n if (trimmed.startsWith('+')) return trimmed\n return `${country.dialCode} ${trimmed}`\n}\n\nexport type PhoneNumberFieldProps = {\n id?: string\n value?: string | null\n onValueChange: (next: string | undefined) => void\n onDigitsChange?: (digits: string | null) => void\n externalError?: string | null\n disabled?: boolean\n autoFocus?: boolean\n ariaLabel?: string\n ariaDescribedBy?: string\n placeholder?: string\n minDigits?: number\n checkingLabel?: string\n duplicateLabel?: (match: PhoneDuplicateMatch) => string\n duplicateLinkLabel?: string\n invalidLabel?: string\n onDuplicateLookup?: (normalizedValue: string) => Promise<PhoneDuplicateMatch | null>\n /**\n * Override the static country list (e.g. limit to specific markets, or supply\n * names already translated by the host). Both the labels and the order of the\n * supplied list are rendered verbatim.\n */\n countries?: PhoneCountry[]\n /** Initial country shown when `value` is empty / unparseable. Defaults to US. */\n defaultCountryIso2?: string\n size?: 32 | 36 | 40\n countryLabel?: string\n renderCountryIcon?: (country: PhoneCountry) => React.ReactNode\n}\n\nconst DEFAULT_MIN_DIGITS = 6\nconst DEFAULT_PLACEHOLDER = '(555) 000-0000'\n\nexport function PhoneNumberField({\n id,\n value,\n onValueChange,\n onDigitsChange,\n externalError,\n disabled = false,\n autoFocus,\n ariaLabel,\n ariaDescribedBy,\n placeholder,\n minDigits = DEFAULT_MIN_DIGITS,\n checkingLabel,\n duplicateLabel,\n duplicateLinkLabel,\n invalidLabel,\n onDuplicateLookup,\n countries: countriesProp,\n defaultCountryIso2,\n size,\n countryLabel,\n renderCountryIcon,\n}: PhoneNumberFieldProps) {\n const t = useT()\n const locale = useOptionalLocale()\n const resolvedInvalidLabel = invalidLabel ?? t(\n 'ui.inputs.phoneNumberField.invalid',\n 'Enter a valid phone number with country code (e.g. +1 212 555 1234)'\n )\n const resolvedCheckingLabel = checkingLabel ?? t(\n 'ui.inputs.phoneNumberField.checking',\n 'Checking for duplicates\u2026'\n )\n const resolvedDuplicateLinkLabel = duplicateLinkLabel ?? t(\n 'ui.inputs.phoneNumberField.duplicateLink',\n 'Open record'\n )\n const resolvedPlaceholder = placeholder ?? DEFAULT_PLACEHOLDER\n const countryOptions = React.useMemo(\n () => buildPhoneCountryOptions(countriesProp, t, locale),\n [countriesProp, t, locale],\n )\n const fallbackCountry = React.useMemo(\n () => (defaultCountryIso2 && findCountryByIso(defaultCountryIso2)) || DEFAULT_COUNTRY,\n [defaultCountryIso2],\n )\n\n const initialCountry = React.useMemo(\n () => parseCountryFromValue(value) ?? fallbackCountry,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n )\n const [country, setCountry] = React.useState<PhoneCountry>(initialCountry)\n const [localNumber, setLocalNumber] = React.useState<string>(() =>\n extractLocalNumber(value, initialCountry.dialCode),\n )\n const [duplicate, setDuplicate] = React.useState<PhoneDuplicateMatch | null>(null)\n const [checking, setChecking] = React.useState(false)\n const [validationHint, setValidationHint] = React.useState<string | null>(null)\n const [focused, setFocused] = React.useState(false)\n const userEditingRef = React.useRef(false)\n\n const externalFieldError = externalError && externalError.trim().length > 0 ? externalError : null\n const errorMessage = externalFieldError ?? validationHint\n const errorId = errorMessage && id ? `${id}-error` : undefined\n const describedBy = [ariaDescribedBy, errorId].filter((part): part is string => Boolean(part)).join(' ') || undefined\n const composedValue = composeValue(country, localNumber)\n\n // Sync local state when `value` updates externally and the user is not editing.\n React.useEffect(() => {\n if (userEditingRef.current) return\n if (value == null || value === '') {\n setLocalNumber('')\n onDigitsChange?.(null)\n return\n }\n const parsed = parseCountryFromValue(value)\n if (parsed) setCountry(parsed)\n const next = extractLocalNumber(value, parsed?.dialCode ?? country.dialCode)\n setLocalNumber(next)\n onDigitsChange?.(extractPhoneDigits(String(value)) || null)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value])\n\n // Debounced duplicate lookup.\n React.useEffect(() => {\n if (!onDuplicateLookup || disabled) {\n setDuplicate(null)\n setChecking(false)\n return\n }\n const digits = extractPhoneDigits(composedValue)\n if (!digits || digits.length < minDigits) {\n setDuplicate(null)\n setChecking(false)\n return\n }\n\n let cancelled = false\n setChecking(true)\n const handle = window.setTimeout(async () => {\n try {\n const match = await onDuplicateLookup(digits)\n if (!cancelled) setDuplicate(match)\n } catch {\n if (!cancelled) setDuplicate(null)\n } finally {\n if (!cancelled) setChecking(false)\n }\n }, 350)\n\n return () => {\n cancelled = true\n window.clearTimeout(handle)\n }\n }, [composedValue, disabled, minDigits, onDuplicateLookup])\n\n const emit = React.useCallback(\n (nextCountry: PhoneCountry, nextLocal: string) => {\n const composed = composeValue(nextCountry, nextLocal)\n onValueChange(composed.length ? composed : undefined)\n onDigitsChange?.(extractPhoneDigits(composed) || null)\n },\n [onDigitsChange, onValueChange],\n )\n\n const handleCountryChange = React.useCallback(\n (iso2: string) => {\n const next = findCountryByIso(iso2)\n if (!next) return\n userEditingRef.current = true\n setCountry(next)\n setValidationHint(null)\n emit(next, localNumber)\n },\n [emit, localNumber],\n )\n\n const handleLocalChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n const next = event.target.value\n userEditingRef.current = true\n setLocalNumber(next)\n setValidationHint(null)\n emit(country, next)\n },\n [country, emit],\n )\n\n const handleBlur = React.useCallback(() => {\n userEditingRef.current = false\n setFocused(false)\n const trimmed = localNumber.trim()\n if (!trimmed) {\n setLocalNumber('')\n setValidationHint(null)\n onValueChange(undefined)\n onDigitsChange?.(null)\n return\n }\n const composed = composeValue(country, trimmed)\n const result = validatePhoneNumber(composed)\n if (result.valid) {\n const normalizedLocal = extractLocalNumber(result.normalized ?? composed, country.dialCode)\n setLocalNumber(normalizedLocal)\n setValidationHint(null)\n onValueChange(result.normalized || undefined)\n onDigitsChange?.(result.digits || null)\n } else {\n setValidationHint(resolvedInvalidLabel)\n onValueChange(composed)\n onDigitsChange?.(result.digits || null)\n }\n }, [country, localNumber, onDigitsChange, onValueChange, resolvedInvalidLabel])\n\n const containerErrorBorder = errorMessage ? 'border-status-error-icon' : 'border-input'\n const containerFocusBorder = errorMessage\n ? 'border-status-error-icon shadow-focus'\n : 'border-brand-violet shadow-focus'\n\n return (\n <div className=\"space-y-2\">\n <div\n className={cn(\n 'flex items-stretch w-full rounded-md border bg-background shadow-xs transition-colors',\n size === 32 && 'h-8',\n size === 36 && 'h-9',\n size === 40 && 'h-10 rounded-lg',\n disabled\n ? 'bg-bg-disabled border-border-disabled cursor-not-allowed'\n : focused\n ? containerFocusBorder\n : `${containerErrorBorder} hover:border-foreground/30`,\n )}\n aria-invalid={errorMessage ? 'true' : undefined}\n data-slot=\"phone-input-wrapper\"\n >\n <Select value={country.iso2} onValueChange={handleCountryChange} disabled={disabled}>\n <SelectTrigger\n aria-label={countryLabel ?? (ariaLabel ? `${ariaLabel} country` : t('ui.inputs.phoneNumberField.country', 'Country code'))}\n size={size}\n className={cn(\n 'h-auto w-auto shrink-0 gap-1.5 rounded-none rounded-l-md border-0 bg-transparent px-2.5 py-2 shadow-none',\n 'hover:bg-muted/40 focus:bg-muted/40 focus-visible:shadow-none focus-visible:border-0',\n 'disabled:bg-transparent disabled:hover:bg-transparent',\n size && 'h-full gap-2 rounded-l-none py-0 pl-2.5 pr-2 [&>svg]:size-5',\n size === 32 && 'pl-1.5 pr-1',\n )}\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n >\n <span className={cn('text-base leading-none', size && 'inline-flex size-5 shrink-0 items-center justify-center text-xl [&_img]:size-full')} aria-hidden=\"true\">{renderCountryIcon ? renderCountryIcon(country) : country.flag}</span>\n <span className={cn(\"text-sm text-foreground tabular-nums\", disabled && \"text-text-disabled\")}>{country.dialCode}</span>\n </SelectTrigger>\n <SelectContent align=\"start\">\n {countryOptions.map(({ country: c, label }) => (\n <SelectItem key={`${c.iso2}-${c.dialCode}`} value={c.iso2}>\n <SelectItemLeading>\n <span className=\"text-base leading-none\">{c.flag}</span>\n </SelectItemLeading>\n <span className=\"flex-1 truncate\">{label}</span>\n <span className=\"ml-2 text-xs text-muted-foreground tabular-nums\">{c.dialCode}</span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <div aria-hidden=\"true\" className=\"w-px self-stretch bg-input\" />\n <input\n type=\"tel\"\n inputMode=\"tel\"\n autoComplete=\"tel-national\"\n value={localNumber}\n onChange={handleLocalChange}\n onBlur={handleBlur}\n onFocus={() => setFocused(true)}\n placeholder={resolvedPlaceholder}\n autoFocus={autoFocus}\n disabled={disabled}\n id={id}\n aria-label={ariaLabel}\n aria-describedby={describedBy}\n aria-invalid={errorMessage ? 'true' : undefined}\n data-crud-focus-target=\"\"\n className={cn(\n 'flex-1 min-w-0 bg-transparent px-3 py-2 text-sm leading-5 outline-none',\n 'placeholder:text-muted-foreground',\n 'disabled:cursor-not-allowed disabled:text-text-disabled disabled:placeholder:text-text-disabled',\n size && 'h-full py-0 pr-2.5',\n size === 32 && 'pl-2 pr-1.5',\n )}\n />\n </div>\n {errorMessage ? (\n <p id={errorId} role=\"alert\" className=\"text-xs text-status-error-text\">{errorMessage}</p>\n ) : null}\n {!disabled && duplicate && duplicateLabel ? (\n <p className=\"text-xs text-status-warning-text\">\n {duplicateLabel(duplicate)}{' '}\n <a className=\"font-medium text-brand-violet underline underline-offset-2\" href={duplicate.href}>\n {resolvedDuplicateLinkLabel}\n </a>\n </p>\n ) : null}\n {!disabled && !duplicate && checking ? (\n <p className=\"text-xs text-muted-foreground\">{resolvedCheckingLabel}</p>\n ) : null}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
useRef,
|
|
9
9
|
useState
|
|
10
10
|
} from "react";
|
|
11
|
+
import { navigateWithPageReload } from "@open-mercato/shared/lib/navigation/pageReload";
|
|
11
12
|
import { apiCall } from "../backend/utils/apiCall.js";
|
|
12
13
|
const PortalCtx = createContext(null);
|
|
13
14
|
function usePortalContext() {
|
|
@@ -87,7 +88,7 @@ function PortalProvider({ orgSlug, children, initialAuth, initialTenant }) {
|
|
|
87
88
|
}
|
|
88
89
|
profileEnrichedRef.current = false;
|
|
89
90
|
setAuthState({ user: null, roles: [], resolvedFeatures: [], isPortalAdmin: false, loading: false, error: null });
|
|
90
|
-
|
|
91
|
+
navigateWithPageReload(`/${orgSlug}/portal/login`);
|
|
91
92
|
}, [orgSlug]);
|
|
92
93
|
const [tenantState] = useState(() => {
|
|
93
94
|
if (initialTenant) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/portal/PortalContext.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport {\n type ReactNode,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react'\nimport type { CustomerAuthResult } from '@open-mercato/shared/modules/customer-auth'\nimport type { CustomerAuthContext } from '@open-mercato/shared/modules/customer-auth'\nimport { apiCall } from '../backend/utils/apiCall'\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\ntype TenantState = {\n tenantId: string | undefined\n organizationId: string | undefined\n organizationName: string | undefined\n loading: boolean\n error: string | null\n}\n\ntype PortalContextValue = {\n auth: CustomerAuthResult & { logout: () => Promise<void> }\n tenant: TenantState\n orgSlug: string\n}\n\nconst PortalCtx = createContext<PortalContextValue | null>(null)\n\n/* ------------------------------------------------------------------ */\n/* Hook */\n/* ------------------------------------------------------------------ */\n\nexport function usePortalContext(): PortalContextValue {\n const ctx = useContext(PortalCtx)\n if (!ctx) throw new Error('usePortalContext must be used inside <PortalProvider>')\n return ctx\n}\n\n/* ------------------------------------------------------------------ */\n/* Provider */\n/* ------------------------------------------------------------------ */\n\ntype InitialTenant = {\n tenantId?: string\n organizationId?: string\n organizationName?: string\n}\n\ntype PortalProviderProps = {\n orgSlug: string\n children: ReactNode\n /** Server-resolved customer auth. When provided, skips client-side fetching. */\n initialAuth?: CustomerAuthContext | null\n /** Server-resolved tenant data. When provided, skips client-side fetching. */\n initialTenant?: InitialTenant\n}\n\n/**\n * Provides auth + tenant state for portal pages.\n *\n * When `initialAuth` and `initialTenant` are provided (from server layout),\n * the context initializes with that data immediately \u2014 no loading state,\n * no client-side fetching, no blink.\n *\n * Falls back to client-side fetching only when initial data is not provided.\n */\nexport function PortalProvider({ orgSlug, children, initialAuth, initialTenant }: PortalProviderProps) {\n const hasServerData = initialAuth !== undefined\n\n /* ---- Auth state ---- */\n const [authState, setAuthState] = useState<CustomerAuthResult>(() => {\n if (hasServerData && initialAuth) {\n // Server-resolved auth \u2014 start fully loaded, no client-side fetch needed\n return {\n user: {\n id: initialAuth.sub,\n email: initialAuth.email,\n displayName: initialAuth.displayName,\n emailVerified: false,\n customerEntityId: initialAuth.customerEntityId ?? null,\n personEntityId: initialAuth.personEntityId ?? null,\n isActive: true,\n lastLoginAt: null,\n createdAt: '',\n },\n roles: [],\n resolvedFeatures: initialAuth.resolvedFeatures,\n isPortalAdmin: initialAuth.isPortalAdmin === true,\n loading: false,\n error: null,\n }\n }\n if (hasServerData && !initialAuth) {\n // Server confirmed: no auth cookie \u2192 not authenticated\n return { user: null, roles: [], resolvedFeatures: [], isPortalAdmin: false, loading: false, error: null }\n }\n // No server data \u2192 need client-side fetch\n return { user: null, roles: [], resolvedFeatures: [], isPortalAdmin: false, loading: true, error: null }\n })\n\n // Guard against re-fetching when the provider persists across navigations\n // and `initialAuth` arrives as a new object reference on each server render.\n const profileEnrichedRef = useRef(false)\n\n // Client-side profile fetch \u2014 only runs when NO server data was provided,\n // or to enrich server-provided auth with full profile (roles, etc.)\n useEffect(() => {\n if (hasServerData && !initialAuth) return // Server said: not authenticated, nothing to fetch\n if (profileEnrichedRef.current) return\n let cancelled = false\n\n async function fetchProfile() {\n try {\n const { ok, status, result: data } = await apiCall<{ ok: boolean; user: CustomerAuthResult['user']; roles: CustomerAuthResult['roles']; resolvedFeatures: string[]; isPortalAdmin: boolean }>('/api/customer_accounts/portal/profile')\n if (cancelled) return\n if (status === 401) {\n setAuthState((prev) => ({ ...prev, loading: false, user: prev.user }))\n return\n }\n if (!ok || !data?.ok) {\n setAuthState((prev) => ({ ...prev, loading: false }))\n return\n }\n profileEnrichedRef.current = true\n setAuthState({\n user: data!.user,\n roles: data!.roles || [],\n resolvedFeatures: data!.resolvedFeatures || [],\n isPortalAdmin: data!.isPortalAdmin || false,\n loading: false,\n error: null,\n })\n } catch {\n if (!cancelled) {\n setAuthState((prev) => ({ ...prev, loading: false }))\n }\n }\n }\n\n fetchProfile()\n return () => { cancelled = true }\n }, [hasServerData, initialAuth])\n\n const logout = useCallback(async () => {\n try {\n await apiCall('/api/customer_accounts/portal/logout', { method: 'POST' })\n } catch {\n // Best-effort logout \u2014 redirect regardless\n }\n profileEnrichedRef.current = false\n setAuthState({ user: null, roles: [], resolvedFeatures: [], isPortalAdmin: false, loading: false, error: null })\n
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport {\n type ReactNode,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react'\nimport type { CustomerAuthResult } from '@open-mercato/shared/modules/customer-auth'\nimport type { CustomerAuthContext } from '@open-mercato/shared/modules/customer-auth'\nimport { navigateWithPageReload } from '@open-mercato/shared/lib/navigation/pageReload'\nimport { apiCall } from '../backend/utils/apiCall'\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\ntype TenantState = {\n tenantId: string | undefined\n organizationId: string | undefined\n organizationName: string | undefined\n loading: boolean\n error: string | null\n}\n\ntype PortalContextValue = {\n auth: CustomerAuthResult & { logout: () => Promise<void> }\n tenant: TenantState\n orgSlug: string\n}\n\nconst PortalCtx = createContext<PortalContextValue | null>(null)\n\n/* ------------------------------------------------------------------ */\n/* Hook */\n/* ------------------------------------------------------------------ */\n\nexport function usePortalContext(): PortalContextValue {\n const ctx = useContext(PortalCtx)\n if (!ctx) throw new Error('usePortalContext must be used inside <PortalProvider>')\n return ctx\n}\n\n/* ------------------------------------------------------------------ */\n/* Provider */\n/* ------------------------------------------------------------------ */\n\ntype InitialTenant = {\n tenantId?: string\n organizationId?: string\n organizationName?: string\n}\n\ntype PortalProviderProps = {\n orgSlug: string\n children: ReactNode\n /** Server-resolved customer auth. When provided, skips client-side fetching. */\n initialAuth?: CustomerAuthContext | null\n /** Server-resolved tenant data. When provided, skips client-side fetching. */\n initialTenant?: InitialTenant\n}\n\n/**\n * Provides auth + tenant state for portal pages.\n *\n * When `initialAuth` and `initialTenant` are provided (from server layout),\n * the context initializes with that data immediately \u2014 no loading state,\n * no client-side fetching, no blink.\n *\n * Falls back to client-side fetching only when initial data is not provided.\n */\nexport function PortalProvider({ orgSlug, children, initialAuth, initialTenant }: PortalProviderProps) {\n const hasServerData = initialAuth !== undefined\n\n /* ---- Auth state ---- */\n const [authState, setAuthState] = useState<CustomerAuthResult>(() => {\n if (hasServerData && initialAuth) {\n // Server-resolved auth \u2014 start fully loaded, no client-side fetch needed\n return {\n user: {\n id: initialAuth.sub,\n email: initialAuth.email,\n displayName: initialAuth.displayName,\n emailVerified: false,\n customerEntityId: initialAuth.customerEntityId ?? null,\n personEntityId: initialAuth.personEntityId ?? null,\n isActive: true,\n lastLoginAt: null,\n createdAt: '',\n },\n roles: [],\n resolvedFeatures: initialAuth.resolvedFeatures,\n isPortalAdmin: initialAuth.isPortalAdmin === true,\n loading: false,\n error: null,\n }\n }\n if (hasServerData && !initialAuth) {\n // Server confirmed: no auth cookie \u2192 not authenticated\n return { user: null, roles: [], resolvedFeatures: [], isPortalAdmin: false, loading: false, error: null }\n }\n // No server data \u2192 need client-side fetch\n return { user: null, roles: [], resolvedFeatures: [], isPortalAdmin: false, loading: true, error: null }\n })\n\n // Guard against re-fetching when the provider persists across navigations\n // and `initialAuth` arrives as a new object reference on each server render.\n const profileEnrichedRef = useRef(false)\n\n // Client-side profile fetch \u2014 only runs when NO server data was provided,\n // or to enrich server-provided auth with full profile (roles, etc.)\n useEffect(() => {\n if (hasServerData && !initialAuth) return // Server said: not authenticated, nothing to fetch\n if (profileEnrichedRef.current) return\n let cancelled = false\n\n async function fetchProfile() {\n try {\n const { ok, status, result: data } = await apiCall<{ ok: boolean; user: CustomerAuthResult['user']; roles: CustomerAuthResult['roles']; resolvedFeatures: string[]; isPortalAdmin: boolean }>('/api/customer_accounts/portal/profile')\n if (cancelled) return\n if (status === 401) {\n setAuthState((prev) => ({ ...prev, loading: false, user: prev.user }))\n return\n }\n if (!ok || !data?.ok) {\n setAuthState((prev) => ({ ...prev, loading: false }))\n return\n }\n profileEnrichedRef.current = true\n setAuthState({\n user: data!.user,\n roles: data!.roles || [],\n resolvedFeatures: data!.resolvedFeatures || [],\n isPortalAdmin: data!.isPortalAdmin || false,\n loading: false,\n error: null,\n })\n } catch {\n if (!cancelled) {\n setAuthState((prev) => ({ ...prev, loading: false }))\n }\n }\n }\n\n fetchProfile()\n return () => { cancelled = true }\n }, [hasServerData, initialAuth])\n\n const logout = useCallback(async () => {\n try {\n await apiCall('/api/customer_accounts/portal/logout', { method: 'POST' })\n } catch {\n // Best-effort logout \u2014 redirect regardless\n }\n profileEnrichedRef.current = false\n setAuthState({ user: null, roles: [], resolvedFeatures: [], isPortalAdmin: false, loading: false, error: null })\n navigateWithPageReload(`/${orgSlug}/portal/login`)\n }, [orgSlug])\n\n /* ---- Tenant state ---- */\n const [tenantState] = useState<TenantState>(() => {\n if (initialTenant) {\n return {\n tenantId: initialTenant.tenantId,\n organizationId: initialTenant.organizationId,\n organizationName: initialTenant.organizationName,\n loading: false,\n error: null,\n }\n }\n return { tenantId: undefined, organizationId: undefined, organizationName: undefined, loading: true, error: null }\n })\n\n // Client-side tenant fetch \u2014 only when no server data\n useEffect(() => {\n if (initialTenant) return // Server already resolved\n // Fallback: kept for backward compat but shouldn't be hit when layout provides data\n }, [initialTenant])\n\n const value: PortalContextValue = {\n auth: { ...authState, logout },\n tenant: tenantState,\n orgSlug,\n }\n\n return <PortalCtx.Provider value={value}>{children}</PortalCtx.Provider>\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useCallback, useEffect, useState } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { navigateWithPageReload } from "@open-mercato/shared/lib/navigation/pageReload";
|
|
4
4
|
import { apiCall } from "../../backend/utils/apiCall.js";
|
|
5
5
|
function useCustomerAuth(orgSlug) {
|
|
6
|
-
const router = useRouter();
|
|
7
6
|
const [state, setState] = useState({
|
|
8
7
|
user: null,
|
|
9
8
|
roles: [],
|
|
@@ -59,8 +58,8 @@ function useCustomerAuth(orgSlug) {
|
|
|
59
58
|
loading: false,
|
|
60
59
|
error: null
|
|
61
60
|
});
|
|
62
|
-
|
|
63
|
-
}, [
|
|
61
|
+
navigateWithPageReload(loginPath);
|
|
62
|
+
}, [loginPath]);
|
|
64
63
|
return { ...state, logout };
|
|
65
64
|
}
|
|
66
65
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/portal/hooks/useCustomerAuth.ts"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport { useCallback, useEffect, useState } from 'react'\nimport
|
|
5
|
-
"mappings": ";AACA,SAAS,aAAa,WAAW,gBAAgB;
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport { useCallback, useEffect, useState } from 'react'\nimport type { CustomerUser, CustomerRole, CustomerAuthResult } from '@open-mercato/shared/modules/customer-auth'\nimport { navigateWithPageReload } from '@open-mercato/shared/lib/navigation/pageReload'\nimport { apiCall } from '../../backend/utils/apiCall'\n\nexport type { CustomerUser, CustomerRole, CustomerAuthResult }\n\n/**\n * Client-side hook for customer portal authentication.\n *\n * Fetches the authenticated customer profile from `/api/customer_accounts/portal/profile`\n * and provides auth state + logout capability.\n *\n * @param orgSlug - Optional organization slug for redirect paths\n *\n * @example\n * ```tsx\n * import { useCustomerAuth } from '@open-mercato/ui/portal/hooks/useCustomerAuth'\n *\n * function MyPortalPage({ orgSlug }: { orgSlug: string }) {\n * const { user, roles, resolvedFeatures, loading, logout } = useCustomerAuth(orgSlug)\n * if (loading) return <LoadingMessage />\n * if (!user) return <Redirect to={`/${orgSlug}/portal/login`} />\n * return <div>Welcome, {user.displayName}</div>\n * }\n * ```\n */\nexport function useCustomerAuth(orgSlug?: string) {\n const [state, setState] = useState<CustomerAuthResult>({\n user: null,\n roles: [],\n resolvedFeatures: [],\n isPortalAdmin: false,\n loading: true,\n error: null,\n })\n\n useEffect(() => {\n let cancelled = false\n\n async function fetchProfile() {\n try {\n const { ok, status, result: data } = await apiCall<{ ok: boolean; user: CustomerAuthResult['user']; roles: CustomerAuthResult['roles']; resolvedFeatures: string[]; isPortalAdmin: boolean; error?: string }>('/api/customer_accounts/portal/profile')\n if (cancelled) return\n\n if (status === 401) {\n setState((prev) => ({ ...prev, loading: false, user: null }))\n return\n }\n\n if (!ok || !data?.ok) {\n setState((prev) => ({ ...prev, loading: false, error: data?.error || `HTTP ${status}` }))\n return\n }\n\n setState({\n user: data.user,\n roles: data.roles || [],\n resolvedFeatures: data.resolvedFeatures || [],\n isPortalAdmin: data.isPortalAdmin || false,\n loading: false,\n error: null,\n })\n } catch {\n if (!cancelled) {\n setState((prev) => ({ ...prev, loading: false, error: 'Network error' }))\n }\n }\n }\n\n fetchProfile()\n return () => { cancelled = true }\n }, [])\n\n const loginPath = orgSlug ? `/${orgSlug}/portal/login` : '/portal/login'\n\n const logout = useCallback(async () => {\n try {\n await apiCall('/api/customer_accounts/portal/logout', { method: 'POST' })\n } catch {\n // Best-effort logout \u2014 redirect regardless\n }\n setState({\n user: null,\n roles: [],\n resolvedFeatures: [],\n isPortalAdmin: false,\n loading: false,\n error: null,\n })\n navigateWithPageReload(loginPath)\n }, [loginPath])\n\n return { ...state, logout }\n}\n"],
|
|
5
|
+
"mappings": ";AACA,SAAS,aAAa,WAAW,gBAAgB;AAEjD,SAAS,8BAA8B;AACvC,SAAS,eAAe;AAwBjB,SAAS,gBAAgB,SAAkB;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA6B;AAAA,IACrD,MAAM;AAAA,IACN,OAAO,CAAC;AAAA,IACR,kBAAkB,CAAC;AAAA,IACnB,eAAe;AAAA,IACf,SAAS;AAAA,IACT,OAAO;AAAA,EACT,CAAC;AAED,YAAU,MAAM;AACd,QAAI,YAAY;AAEhB,mBAAe,eAAe;AAC5B,UAAI;AACF,cAAM,EAAE,IAAI,QAAQ,QAAQ,KAAK,IAAI,MAAM,QAAmK,uCAAuC;AACrP,YAAI,UAAW;AAEf,YAAI,WAAW,KAAK;AAClB,mBAAS,CAAC,UAAU,EAAE,GAAG,MAAM,SAAS,OAAO,MAAM,KAAK,EAAE;AAC5D;AAAA,QACF;AAEA,YAAI,CAAC,MAAM,CAAC,MAAM,IAAI;AACpB,mBAAS,CAAC,UAAU,EAAE,GAAG,MAAM,SAAS,OAAO,OAAO,MAAM,SAAS,QAAQ,MAAM,GAAG,EAAE;AACxF;AAAA,QACF;AAEA,iBAAS;AAAA,UACP,MAAM,KAAK;AAAA,UACX,OAAO,KAAK,SAAS,CAAC;AAAA,UACtB,kBAAkB,KAAK,oBAAoB,CAAC;AAAA,UAC5C,eAAe,KAAK,iBAAiB;AAAA,UACrC,SAAS;AAAA,UACT,OAAO;AAAA,QACT,CAAC;AAAA,MACH,QAAQ;AACN,YAAI,CAAC,WAAW;AACd,mBAAS,CAAC,UAAU,EAAE,GAAG,MAAM,SAAS,OAAO,OAAO,gBAAgB,EAAE;AAAA,QAC1E;AAAA,MACF;AAAA,IACF;AAEA,iBAAa;AACb,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAK;AAAA,EAClC,GAAG,CAAC,CAAC;AAEL,QAAM,YAAY,UAAU,IAAI,OAAO,kBAAkB;AAEzD,QAAM,SAAS,YAAY,YAAY;AACrC,QAAI;AACF,YAAM,QAAQ,wCAAwC,EAAE,QAAQ,OAAO,CAAC;AAAA,IAC1E,QAAQ;AAAA,IAER;AACA,aAAS;AAAA,MACP,MAAM;AAAA,MACN,OAAO,CAAC;AAAA,MACR,kBAAkB,CAAC;AAAA,MACnB,eAAe;AAAA,MACf,SAAS;AAAA,MACT,OAAO;AAAA,IACT,CAAC;AACD,2BAAuB,SAAS;AAAA,EAClC,GAAG,CAAC,SAAS,CAAC;AAEd,SAAO,EAAE,GAAG,OAAO,OAAO;AAC5B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/ui",
|
|
3
|
-
"version": "0.8.1-develop.
|
|
3
|
+
"version": "0.8.1-develop.7294.1.0ef99fc518",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -155,14 +155,14 @@
|
|
|
155
155
|
"remark-gfm": "^4.0.1"
|
|
156
156
|
},
|
|
157
157
|
"peerDependencies": {
|
|
158
|
-
"@open-mercato/shared": "0.8.1-develop.
|
|
158
|
+
"@open-mercato/shared": "0.8.1-develop.7294.1.0ef99fc518",
|
|
159
159
|
"react": ">=18.0.0",
|
|
160
160
|
"react-dom": ">=18.0.0",
|
|
161
161
|
"react-is": ">=18.0.0"
|
|
162
162
|
},
|
|
163
163
|
"devDependencies": {
|
|
164
164
|
"@figma/code-connect": "^1.3.4",
|
|
165
|
-
"@open-mercato/shared": "0.8.1-develop.
|
|
165
|
+
"@open-mercato/shared": "0.8.1-develop.7294.1.0ef99fc518",
|
|
166
166
|
"@testing-library/dom": "^10.4.1",
|
|
167
167
|
"@testing-library/jest-dom": "^7.0.1",
|
|
168
168
|
"@testing-library/react": "^16.3.3",
|
|
@@ -1426,16 +1426,43 @@ export function DataTable<T extends RowData>({
|
|
|
1426
1426
|
// changes once the user actually changes something after that point.
|
|
1427
1427
|
const [viewBaseline, setViewBaselineState] = React.useState<PerspectiveSettings>(() => mergedInitialSettings ?? {})
|
|
1428
1428
|
const viewBaselineInitializedRef = React.useRef(Boolean(mergedInitialSettings))
|
|
1429
|
+
// What the `meta.hidden` pass below hid on its own, since a declared default is not a
|
|
1430
|
+
// user edit and must reach the baseline too. `normalizeVisibility` keeps every `false`,
|
|
1431
|
+
// so a default the stored view never named would otherwise read as a change nobody
|
|
1432
|
+
// made and light the Save-view affordance the moment the table mounts (#5117).
|
|
1433
|
+
const autoHiddenVisibilityRef = React.useRef<VisibilityState>({})
|
|
1429
1434
|
const setViewBaseline = React.useCallback((settings: PerspectiveSettings) => {
|
|
1430
1435
|
const initialized = viewBaselineInitializedRef.current
|
|
1431
1436
|
viewBaselineInitializedRef.current = true
|
|
1437
|
+
// Auto-hidden defaults win over `settings` for the keys they decided: this covers the
|
|
1438
|
+
// baseline's first seeding, which closes over `currentViewSettings` from the render
|
|
1439
|
+
// that *scheduled* the meta.hidden hide, not the render where it actually took effect
|
|
1440
|
+
// — so `settings.columnVisibility` here is stale for exactly those keys. A real stored
|
|
1441
|
+
// view never has a key in common with `autoHidden` (the meta.hidden pass skips every
|
|
1442
|
+
// column `visibilityDecidedColumnIds` already seeded from that view), so this never
|
|
1443
|
+
// overrides a genuine user/view decision.
|
|
1444
|
+
const autoHidden = autoHiddenVisibilityRef.current
|
|
1445
|
+
const next = Object.keys(autoHidden).length
|
|
1446
|
+
? { ...settings, columnVisibility: { ...(settings.columnVisibility ?? {}), ...autoHidden } }
|
|
1447
|
+
: settings
|
|
1432
1448
|
// Compared by value, not by identity: the callers hand over freshly
|
|
1433
1449
|
// sanitized objects (a new one on every render), so storing them blindly
|
|
1434
1450
|
// would let an effect keyed on those settings re-trigger itself forever.
|
|
1435
1451
|
setViewBaselineState((previous) => (
|
|
1436
|
-
initialized && diffPerspectiveSettings(previous,
|
|
1452
|
+
initialized && diffPerspectiveSettings(previous, next).length === 0 ? previous : next
|
|
1437
1453
|
))
|
|
1438
1454
|
}, [])
|
|
1455
|
+
// Late arrivals hit this instead: the baseline already exists, so the entries are
|
|
1456
|
+
// merged into it in place rather than waiting for the next `setViewBaseline` call.
|
|
1457
|
+
const foldAutoHiddenIntoBaseline = React.useCallback((entries: VisibilityState) => {
|
|
1458
|
+
autoHiddenVisibilityRef.current = { ...autoHiddenVisibilityRef.current, ...entries }
|
|
1459
|
+
if (!viewBaselineInitializedRef.current) return
|
|
1460
|
+
setViewBaselineState((previous) => {
|
|
1461
|
+
const current = previous.columnVisibility ?? {}
|
|
1462
|
+
if (Object.keys(entries).every((key) => key in current)) return previous
|
|
1463
|
+
return { ...previous, columnVisibility: { ...entries, ...current } }
|
|
1464
|
+
})
|
|
1465
|
+
}, [])
|
|
1439
1466
|
|
|
1440
1467
|
const perspectiveFeatureQuery = useQuery<{ use: boolean; roleDefaults: boolean }>({
|
|
1441
1468
|
queryKey: ['feature-check', 'perspectives'],
|
|
@@ -1906,9 +1933,6 @@ export function DataTable<T extends RowData>({
|
|
|
1906
1933
|
})
|
|
1907
1934
|
}, [table, mergedColumns])
|
|
1908
1935
|
|
|
1909
|
-
// A stored perspective seeds `columnVisibility` at mount and wins outright over the
|
|
1910
|
-
// `meta.hidden` defaults, so the auto-hide pass is skipped entirely in that case.
|
|
1911
|
-
const visibilitySeededByStoredSettings = React.useRef(Boolean(mergedInitialSettings?.columnVisibility))
|
|
1912
1936
|
// Auto-hiding is a per-column default, applied once per column — not an enforcement.
|
|
1913
1937
|
// It cannot be latched by a single has-run boolean: columns arrive in waves, because
|
|
1914
1938
|
// custom-field columns are built from definitions fetched asynchronously. The first
|
|
@@ -1919,26 +1943,51 @@ export function DataTable<T extends RowData>({
|
|
|
1919
1943
|
// `handleColumnChooserToggle` *deletes* a column's entry when the user turns it back on,
|
|
1920
1944
|
// so a re-shown column is indistinguishable from one never seen and would be hidden again
|
|
1921
1945
|
// on the next wave. Hence an explicit per-column record of what this pass has applied.
|
|
1922
|
-
|
|
1946
|
+
// Seeded from the stored view's own keys, because a view decides only the columns it
|
|
1947
|
+
// actually names (#5117): latching the whole pass on "the view carried some visibility"
|
|
1948
|
+
// left every later-registering column undecided, and undecided renders visible.
|
|
1949
|
+
const visibilityDecidedColumnIds = React.useRef<Set<string>>(
|
|
1950
|
+
new Set(Object.keys(mergedInitialSettings?.columnVisibility ?? {})),
|
|
1951
|
+
)
|
|
1952
|
+
// Bumped whenever the record is re-seeded, so switching views re-evaluates the defaults
|
|
1953
|
+
// instead of waiting for the next wave of columns; the record itself is a ref, written
|
|
1954
|
+
// from inside the pass, and so cannot trigger it.
|
|
1955
|
+
const [visibilityDefaultsPass, setVisibilityDefaultsPass] = React.useState(0)
|
|
1923
1956
|
React.useEffect(() => {
|
|
1924
|
-
if (visibilitySeededByStoredSettings.current) return
|
|
1925
1957
|
const hidden: VisibilityState = {}
|
|
1926
1958
|
table.getAllLeafColumns().forEach((column) => {
|
|
1959
|
+
if (visibilityDecidedColumnIds.current.has(column.id)) return
|
|
1927
1960
|
const hiddenMeta = (column.columnDef as any)?.meta?.hidden
|
|
1928
|
-
|
|
1961
|
+
// Recorded only once it has actually applied a default. Marking a column decided
|
|
1962
|
+
// merely for having been seen would strand a def whose `meta.hidden` resolves in a
|
|
1963
|
+
// later wave — an injected column widget — as permanently undecided.
|
|
1964
|
+
if (!hiddenMeta) return
|
|
1965
|
+
visibilityDecidedColumnIds.current.add(column.id)
|
|
1929
1966
|
hidden[column.id] = false
|
|
1930
|
-
autoHiddenColumnIds.current.add(column.id)
|
|
1931
1967
|
})
|
|
1932
1968
|
if (!Object.keys(hidden).length) return
|
|
1933
1969
|
setColumnVisibility((prev) => ({ ...hidden, ...prev }))
|
|
1934
|
-
|
|
1970
|
+
foldAutoHiddenIntoBaseline(hidden)
|
|
1971
|
+
}, [table, mergedColumns, visibilityDefaultsPass, foldAutoHiddenIntoBaseline])
|
|
1935
1972
|
|
|
1936
1973
|
const getCurrentSettings = React.useCallback((): PerspectiveSettings => {
|
|
1974
|
+
// A union, in this order, of two partial pictures (#5117). `columnVisibility` alone
|
|
1975
|
+
// is sparse — it holds only what the user toggled plus what a view restored, and an
|
|
1976
|
+
// absent key renders visible, so a saved view stored no decision for any column that
|
|
1977
|
+
// registered later. The live column set alone is lossy in the other direction: it
|
|
1978
|
+
// drops a stored decision for a column that has not registered yet, throwing away a
|
|
1979
|
+
// hidden `cf_*` if the user saves during the custom-field hydration window. Taking
|
|
1980
|
+
// the live columns second makes the map dense over everything on screen and records
|
|
1981
|
+
// a column the user turned back *on* as an explicit `true` — which the sparse map
|
|
1982
|
+
// could not express, since `handleColumnChooserToggle` deletes the entry — so the
|
|
1983
|
+
// default pass above leaves it alone on reload. `perspectiveDirty` normalizes `true`
|
|
1984
|
+
// away, so a dense map still compares equal to a sparse one stored on the server.
|
|
1937
1985
|
const visibility: Record<string, boolean> = {}
|
|
1938
1986
|
for (const [key, value] of Object.entries(columnVisibility)) {
|
|
1939
|
-
if (typeof
|
|
1940
|
-
|
|
1941
|
-
|
|
1987
|
+
if (typeof value === 'boolean') visibility[key] = value
|
|
1988
|
+
}
|
|
1989
|
+
for (const column of table.getAllLeafColumns()) {
|
|
1990
|
+
visibility[column.id] = column.getIsVisible()
|
|
1942
1991
|
}
|
|
1943
1992
|
// When the host page wires an advanced-filter tree, persist that as the
|
|
1944
1993
|
// single source of truth for `filters`. The tree wins over legacy
|
|
@@ -1967,7 +2016,7 @@ export function DataTable<T extends RowData>({
|
|
|
1967
2016
|
searchValue,
|
|
1968
2017
|
}
|
|
1969
2018
|
return sanitizePerspectiveSettings(candidate) ?? {}
|
|
1970
|
-
}, [columnOrder, columnVisibility, columnSizing, sorting, filterValues, searchValue, advancedFilter])
|
|
2019
|
+
}, [table, columnOrder, columnVisibility, columnSizing, sorting, filterValues, searchValue, advancedFilter])
|
|
1971
2020
|
|
|
1972
2021
|
const applyPerspectiveSettings = React.useCallback((
|
|
1973
2022
|
settings: PerspectiveSettings,
|
|
@@ -1983,6 +2032,10 @@ export function DataTable<T extends RowData>({
|
|
|
1983
2032
|
},
|
|
1984
2033
|
) => {
|
|
1985
2034
|
const normalized = sanitizePerspectiveSettings(settings) ?? {}
|
|
2035
|
+
// The applied settings replace the baseline outright, so the defaults hidden under
|
|
2036
|
+
// the *previous* view must not leak into it. The pass below re-runs and re-folds
|
|
2037
|
+
// whatever this view leaves undecided.
|
|
2038
|
+
autoHiddenVisibilityRef.current = {}
|
|
1986
2039
|
// `preserveAdvancedFilter` leaves the host's filter state untouched, so the
|
|
1987
2040
|
// applied settings' `filters` never become the live ones — keeping the live
|
|
1988
2041
|
// payload here is what stops the mount-time restore from reporting a
|
|
@@ -1998,6 +2051,12 @@ export function DataTable<T extends RowData>({
|
|
|
1998
2051
|
const ids = table.getAllLeafColumns().map((column) => column.id)
|
|
1999
2052
|
if (ids.length) setColumnOrder(ids)
|
|
2000
2053
|
}
|
|
2054
|
+
// Re-seed the per-column record so the newly applied view decides exactly the
|
|
2055
|
+
// columns it names and nothing else (#5117). Clearing to "No view" empties it, which
|
|
2056
|
+
// is what lets the `meta.hidden` defaults come back instead of staying suppressed for
|
|
2057
|
+
// the rest of the session by a view the user has since dismissed.
|
|
2058
|
+
visibilityDecidedColumnIds.current = new Set(Object.keys(normalized.columnVisibility ?? {}))
|
|
2059
|
+
setVisibilityDefaultsPass((pass) => pass + 1)
|
|
2001
2060
|
if (normalized.columnVisibility) setColumnVisibility(normalized.columnVisibility)
|
|
2002
2061
|
else setColumnVisibility({})
|
|
2003
2062
|
if (normalized.sorting) {
|