Pārlūkot izejas kodu

feat(dashboard): Use dynamic schema-driven languages and currencies (#4111)

Sergi Gonzalez 20 stundas atpakaļ
vecāks
revīzija
888e9467da

+ 1 - 1
packages/dashboard/src/app/routes/_authenticated/_global-settings/global-settings.tsx

@@ -23,7 +23,7 @@ import { Trans, useLingui } from '@lingui/react/macro';
 import { createFileRoute, useNavigate } from '@tanstack/react-router';
 import { toast } from 'sonner';
 import { globalSettingsDocument, updateGlobalSettingsDocument } from './global-settings.graphql.js';
-import { globalLanguageCodes } from '@/vdb/utils/global-languages.js';
+import { schemaLanguageCodes as globalLanguageCodes } from '@/vdb/graphql/schema-enums.js';
 
 const pageId = 'global-settings';
 

+ 16 - 17
packages/dashboard/src/lib/components/layout/channel-switcher.tsx

@@ -13,8 +13,8 @@ import {
     DropdownMenuSubTrigger,
     DropdownMenuTrigger,
 } from '@/vdb/components/ui/dropdown-menu.js';
-import { SidebarMenu, SidebarMenuButton, SidebarMenuItem, useSidebar } from '@/vdb/components/ui/sidebar.js';
 import { ScrollArea } from '@/vdb/components/ui/scroll-area.js';
+import { SidebarMenu, SidebarMenuButton, SidebarMenuItem, useSidebar } from '@/vdb/components/ui/sidebar.js';
 import { DEFAULT_CHANNEL_CODE } from '@/vdb/constants.js';
 import { useChannel } from '@/vdb/hooks/use-channel.js';
 import { useLocalFormat } from '@/vdb/hooks/use-local-format.js';
@@ -64,9 +64,7 @@ export function ChannelSwitcher() {
     const hasMultipleLanguages = availableLanguages.length > 1;
 
     // Currently selected channel is displayed separately so filter it out of the list
-    const orderedChannels = displayChannel
-        ? channels.filter(ch => ch.id !== displayChannel.id)
-        : channels;
+    const orderedChannels = displayChannel ? channels.filter(ch => ch.id !== displayChannel.id) : channels;
 
     // Sort language codes by their formatted names and map to code and label
     const sortedLanguages = useSortedLanguages(displayChannel?.availableLanguageCodes);
@@ -81,12 +79,9 @@ export function ChannelSwitcher() {
         }
     }, [activeChannel, contentLanguage]);
 
-    const renderChannel = (channel: typeof channels[number]) => (
+    const renderChannel = (channel: (typeof channels)[number]) => (
         <div key={channel.code}>
-            <DropdownMenuItem
-                onClick={() => setActiveChannel(channel.id)}
-                className="gap-2 p-2"
-            >
+            <DropdownMenuItem onClick={() => setActiveChannel(channel.id)} className="gap-2 p-2">
                 <div
                     className={cn(
                         'flex size-8 items-center justify-center rounded border',
@@ -140,7 +135,7 @@ export function ChannelSwitcher() {
                             </SidebarMenuButton>
                         </DropdownMenuTrigger>
                         <DropdownMenuContent
-                            className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg pt-0 pr-0"
+                            className="w-(--radix-dropdown-menu-trigger-width) min-w-72 rounded-lg pt-0 pr-0"
                             align="start"
                             side={isMobile ? 'bottom' : 'right'}
                             sideOffset={4}
@@ -156,10 +151,14 @@ export function ChannelSwitcher() {
                                             {/* Show language sub-menu for the current channel */}
                                             <DropdownMenuSub>
                                                 <DropdownMenuSubTrigger className="gap-2 p-2 ps-4">
-                                                    <Languages className="w-4 h-4" />
+                                                    <Languages className="w-4 h-4 shrink-0" />
                                                     <div className="flex gap-1 ms-2">
-                                                        <span className="text-muted-foreground">Content: </span>
-                                                        {formatLanguageName(contentLanguage)}
+                                                        <span className="text-muted-foreground shrink-0">
+                                                            <Trans>Content:</Trans>
+                                                        </span>
+                                                        <span className="truncate">
+                                                            {formatLanguageName(contentLanguage)}
+                                                        </span>
                                                     </div>
                                                 </DropdownMenuSubTrigger>
                                                 <DropdownMenuSubContent>
@@ -169,14 +168,14 @@ export function ChannelSwitcher() {
                                                             onClick={() => setContentLanguage(languageCode)}
                                                             className={`gap-2 p-2 ${contentLanguage === languageCode ? 'bg-accent' : ''}`}
                                                         >
-                                                            <div className="flex w-6 h-5 items-center justify-center rounded border">
-                                                                <span className="truncate font-medium text-xs">
+                                                            <div className="flex w-fit min-w-9 px-1.5 h-5 items-center justify-center rounded border shrink-0">
+                                                                <span className="font-medium text-xs">
                                                                     {languageCode.toUpperCase()}
                                                                 </span>
                                                             </div>
-                                                            <span>{label}</span>
+                                                            <span className="truncate flex-1">{label}</span>
                                                             {contentLanguage === languageCode && (
-                                                                <span className="ms-auto text-xs text-muted-foreground">
+                                                                <span className="ms-auto text-xs text-muted-foreground shrink-0">
                                                                     <Trans context="active language">
                                                                         Active
                                                                     </Trans>

+ 1 - 1
packages/dashboard/src/lib/components/layout/manage-languages-dialog.tsx

@@ -18,7 +18,7 @@ import { useChannel } from '@/vdb/hooks/use-channel.js';
 import { useLocalFormat } from '@/vdb/hooks/use-local-format.js';
 import { usePermissions } from '@/vdb/hooks/use-permissions.js';
 import { useSortedLanguages } from '@/vdb/hooks/use-sorted-languages.js';
-import { globalLanguageCodes } from '@/vdb/utils/global-languages.js';
+import { schemaLanguageCodes as globalLanguageCodes } from '@/vdb/graphql/schema-enums.js';
 import { Trans } from '@lingui/react/macro';
 import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
 import { AlertCircle, Lock } from 'lucide-react';

+ 7 - 319
packages/dashboard/src/lib/constants.ts

@@ -1,3 +1,5 @@
+import { schemaCurrencyCodes } from '@/vdb/graphql/schema-enums.js';
+
 export const NEW_ENTITY_PATH = 'new';
 export const AUTHENTICATED_ROUTE_PREFIX = '/_authenticated';
 export const DEFAULT_CHANNEL_CODE = '__default_channel__';
@@ -14,323 +16,9 @@ export const LS_KEY_SHIPPING_TEST_ORDER = 'vendure-shipping-test-order';
 export const LS_KEY_SHIPPING_TEST_ADDRESS = 'vendure-shipping-test-address';
 
 /**
- * This is copied from the generated types from @vendure/common/lib/generated-types.d.ts
- * It is used to provide a list of available currency codes for the user to select from.
- * esbuild currently does not support import enums.
+ * @description
+ * Runtime access to the CurrencyCode enum values derived from the schema.
  */
-export const CurrencyCode = {
-    /** United Arab Emirates dirham */
-    AED: 'AED',
-    /** Afghan afghani */
-    AFN: 'AFN',
-    /** Albanian lek */
-    ALL: 'ALL',
-    /** Armenian dram */
-    AMD: 'AMD',
-    /** Netherlands Antillean guilder */
-    ANG: 'ANG',
-    /** Angolan kwanza */
-    AOA: 'AOA',
-    /** Argentine peso */
-    ARS: 'ARS',
-    /** Australian dollar */
-    AUD: 'AUD',
-    /** Aruban florin */
-    AWG: 'AWG',
-    /** Azerbaijani manat */
-    AZN: 'AZN',
-    /** Bosnia and Herzegovina convertible mark */
-    BAM: 'BAM',
-    /** Barbados dollar */
-    BBD: 'BBD',
-    /** Bangladeshi taka */
-    BDT: 'BDT',
-    /** Bulgarian lev */
-    BGN: 'BGN',
-    /** Bahraini dinar */
-    BHD: 'BHD',
-    /** Burundian franc */
-    BIF: 'BIF',
-    /** Bermudian dollar */
-    BMD: 'BMD',
-    /** Brunei dollar */
-    BND: 'BND',
-    /** Boliviano */
-    BOB: 'BOB',
-    /** Brazilian real */
-    BRL: 'BRL',
-    /** Bahamian dollar */
-    BSD: 'BSD',
-    /** Bhutanese ngultrum */
-    BTN: 'BTN',
-    /** Botswana pula */
-    BWP: 'BWP',
-    /** Belarusian ruble */
-    BYN: 'BYN',
-    /** Belize dollar */
-    BZD: 'BZD',
-    /** Canadian dollar */
-    CAD: 'CAD',
-    /** Congolese franc */
-    CDF: 'CDF',
-    /** Swiss franc */
-    CHF: 'CHF',
-    /** Chilean peso */
-    CLP: 'CLP',
-    /** Renminbi (Chinese) yuan */
-    CNY: 'CNY',
-    /** Colombian peso */
-    COP: 'COP',
-    /** Costa Rican colon */
-    CRC: 'CRC',
-    /** Cuban convertible peso */
-    CUC: 'CUC',
-    /** Cuban peso */
-    CUP: 'CUP',
-    /** Cape Verde escudo */
-    CVE: 'CVE',
-    /** Czech koruna */
-    CZK: 'CZK',
-    /** Djiboutian franc */
-    DJF: 'DJF',
-    /** Danish krone */
-    DKK: 'DKK',
-    /** Dominican peso */
-    DOP: 'DOP',
-    /** Algerian dinar */
-    DZD: 'DZD',
-    /** Egyptian pound */
-    EGP: 'EGP',
-    /** Eritrean nakfa */
-    ERN: 'ERN',
-    /** Ethiopian birr */
-    ETB: 'ETB',
-    /** Euro */
-    EUR: 'EUR',
-    /** Fiji dollar */
-    FJD: 'FJD',
-    /** Falkland Islands pound */
-    FKP: 'FKP',
-    /** Pound sterling */
-    GBP: 'GBP',
-    /** Georgian lari */
-    GEL: 'GEL',
-    /** Ghanaian cedi */
-    GHS: 'GHS',
-    /** Gibraltar pound */
-    GIP: 'GIP',
-    /** Gambian dalasi */
-    GMD: 'GMD',
-    /** Guinean franc */
-    GNF: 'GNF',
-    /** Guatemalan quetzal */
-    GTQ: 'GTQ',
-    /** Guyanese dollar */
-    GYD: 'GYD',
-    /** Hong Kong dollar */
-    HKD: 'HKD',
-    /** Honduran lempira */
-    HNL: 'HNL',
-    /** Croatian kuna */
-    HRK: 'HRK',
-    /** Haitian gourde */
-    HTG: 'HTG',
-    /** Hungarian forint */
-    HUF: 'HUF',
-    /** Indonesian rupiah */
-    IDR: 'IDR',
-    /** Israeli new shekel */
-    ILS: 'ILS',
-    /** Indian rupee */
-    INR: 'INR',
-    /** Iraqi dinar */
-    IQD: 'IQD',
-    /** Iranian rial */
-    IRR: 'IRR',
-    /** Icelandic króna */
-    ISK: 'ISK',
-    /** Jamaican dollar */
-    JMD: 'JMD',
-    /** Jordanian dinar */
-    JOD: 'JOD',
-    /** Japanese yen */
-    JPY: 'JPY',
-    /** Kenyan shilling */
-    KES: 'KES',
-    /** Kyrgyzstani som */
-    KGS: 'KGS',
-    /** Cambodian riel */
-    KHR: 'KHR',
-    /** Comoro franc */
-    KMF: 'KMF',
-    /** North Korean won */
-    KPW: 'KPW',
-    /** South Korean won */
-    KRW: 'KRW',
-    /** Kuwaiti dinar */
-    KWD: 'KWD',
-    /** Cayman Islands dollar */
-    KYD: 'KYD',
-    /** Kazakhstani tenge */
-    KZT: 'KZT',
-    /** Lao kip */
-    LAK: 'LAK',
-    /** Lebanese pound */
-    LBP: 'LBP',
-    /** Sri Lankan rupee */
-    LKR: 'LKR',
-    /** Liberian dollar */
-    LRD: 'LRD',
-    /** Lesotho loti */
-    LSL: 'LSL',
-    /** Libyan dinar */
-    LYD: 'LYD',
-    /** Moroccan dirham */
-    MAD: 'MAD',
-    /** Moldovan leu */
-    MDL: 'MDL',
-    /** Malagasy ariary */
-    MGA: 'MGA',
-    /** Macedonian denar */
-    MKD: 'MKD',
-    /** Myanmar kyat */
-    MMK: 'MMK',
-    /** Mongolian tögrög */
-    MNT: 'MNT',
-    /** Macanese pataca */
-    MOP: 'MOP',
-    /** Mauritanian ouguiya */
-    MRU: 'MRU',
-    /** Mauritian rupee */
-    MUR: 'MUR',
-    /** Maldivian rufiyaa */
-    MVR: 'MVR',
-    /** Malawian kwacha */
-    MWK: 'MWK',
-    /** Mexican peso */
-    MXN: 'MXN',
-    /** Malaysian ringgit */
-    MYR: 'MYR',
-    /** Mozambican metical */
-    MZN: 'MZN',
-    /** Namibian dollar */
-    NAD: 'NAD',
-    /** Nigerian naira */
-    NGN: 'NGN',
-    /** Nicaraguan córdoba */
-    NIO: 'NIO',
-    /** Norwegian krone */
-    NOK: 'NOK',
-    /** Nepalese rupee */
-    NPR: 'NPR',
-    /** New Zealand dollar */
-    NZD: 'NZD',
-    /** Omani rial */
-    OMR: 'OMR',
-    /** Panamanian balboa */
-    PAB: 'PAB',
-    /** Peruvian sol */
-    PEN: 'PEN',
-    /** Papua New Guinean kina */
-    PGK: 'PGK',
-    /** Philippine peso */
-    PHP: 'PHP',
-    /** Pakistani rupee */
-    PKR: 'PKR',
-    /** Polish złoty */
-    PLN: 'PLN',
-    /** Paraguayan guaraní */
-    PYG: 'PYG',
-    /** Qatari riyal */
-    QAR: 'QAR',
-    /** Romanian leu */
-    RON: 'RON',
-    /** Serbian dinar */
-    RSD: 'RSD',
-    /** Russian ruble */
-    RUB: 'RUB',
-    /** Rwandan franc */
-    RWF: 'RWF',
-    /** Saudi riyal */
-    SAR: 'SAR',
-    /** Solomon Islands dollar */
-    SBD: 'SBD',
-    /** Seychelles rupee */
-    SCR: 'SCR',
-    /** Sudanese pound */
-    SDG: 'SDG',
-    /** Swedish krona/kronor */
-    SEK: 'SEK',
-    /** Singapore dollar */
-    SGD: 'SGD',
-    /** Saint Helena pound */
-    SHP: 'SHP',
-    /** Sierra Leonean leone */
-    SLL: 'SLL',
-    /** Somali shilling */
-    SOS: 'SOS',
-    /** Surinamese dollar */
-    SRD: 'SRD',
-    /** South Sudanese pound */
-    SSP: 'SSP',
-    /** São Tomé and Príncipe dobra */
-    STN: 'STN',
-    /** Salvadoran colón */
-    SVC: 'SVC',
-    /** Syrian pound */
-    SYP: 'SYP',
-    /** Swazi lilangeni */
-    SZL: 'SZL',
-    /** Thai baht */
-    THB: 'THB',
-    /** Tajikistani somoni */
-    TJS: 'TJS',
-    /** Turkmenistan manat */
-    TMT: 'TMT',
-    /** Tunisian dinar */
-    TND: 'TND',
-    /** Tongan paʻanga */
-    TOP: 'TOP',
-    /** Turkish lira */
-    TRY: 'TRY',
-    /** Trinidad and Tobago dollar */
-    TTD: 'TTD',
-    /** New Taiwan dollar */
-    TWD: 'TWD',
-    /** Tanzanian shilling */
-    TZS: 'TZS',
-    /** Ukrainian hryvnia */
-    UAH: 'UAH',
-    /** Ugandan shilling */
-    UGX: 'UGX',
-    /** United States dollar */
-    USD: 'USD',
-    /** Uruguayan peso */
-    UYU: 'UYU',
-    /** Uzbekistan som */
-    UZS: 'UZS',
-    /** Venezuelan bolívar soberano */
-    VES: 'VES',
-    /** Vietnamese đồng */
-    VND: 'VND',
-    /** Vanuatu vatu */
-    VUV: 'VUV',
-    /** Samoan tala */
-    WST: 'WST',
-    /** CFA franc BEAC */
-    XAF: 'XAF',
-    /** East Caribbean dollar */
-    XCD: 'XCD',
-    /** CFA franc BCEAO */
-    XOF: 'XOF',
-    /** CFP franc (franc Pacifique) */
-    XPF: 'XPF',
-    /** Yemeni rial */
-    YER: 'YER',
-    /** South African rand */
-    ZAR: 'ZAR',
-    /** Zambian kwacha */
-    ZMW: 'ZMW',
-    /** Zimbabwean dollar */
-    ZWL: 'ZWL',
-};
+export const CurrencyCode = Object.fromEntries(
+    schemaCurrencyCodes.map((code: string) => [code, code]),
+) as Record<string, string>;

+ 13 - 0
packages/dashboard/src/lib/graphql/schema-enums.ts

@@ -0,0 +1,13 @@
+import { schemaInfo } from 'virtual:admin-api-schema';
+
+/**
+ * @description
+ * Runtime access to the LanguageCode enum values defined in the GraphQL schema.
+ */
+export const schemaLanguageCodes = schemaInfo.enums.LanguageCode ?? [];
+
+/**
+ * @description
+ * Runtime access to the CurrencyCode enum values defined in the GraphQL schema.
+ */
+export const schemaCurrencyCodes = schemaInfo.enums.CurrencyCode ?? [];

+ 0 - 268
packages/dashboard/src/lib/utils/global-languages.ts

@@ -1,268 +0,0 @@
-export const globalLanguageCodes = [
-    /** Afrikaans */
-    'af',
-    /** Akan */
-    'ak',
-    /** Amharic */
-    'am',
-    /** Arabic */
-    'ar',
-    /** Assamese */
-    'as',
-    /** Azerbaijani */
-    'az',
-    /** Belarusian */
-    'be',
-    /** Bulgarian */
-    'bg',
-    /** Bambara */
-    'bm',
-    /** Bangla */
-    'bn',
-    /** Breton */
-    'br',
-    /** Bosnian */
-    'bs',
-    /** Catalan */
-    'ca',
-    /** Chechen */
-    'co',
-    /** Czech */
-    'cs',
-    /** Welsh */
-    'cy',
-    /** Danish */
-    'da',
-    /** German */
-    'de',
-    /** Ewe */
-    'ee',
-    /** Greek */
-    'el',
-    /** English */
-    'en',
-    /** Esperanto */
-    'eo',
-    /** Spanish */
-    'es',
-    /** European Spanish */
-    'es_ES',
-    /** Mexican Spanish */
-    'es_MX',
-    /** Estonian */
-    'et',
-    /** Basque */
-    'eu',
-    /** Persian */
-    'fa',
-    /** Dari */
-    'fa_AF',
-    /** Finnish */
-    'fi',
-    /** Faroese */
-    'fo',
-    /** French */
-    'fr',
-    /** Canadian French */
-    'fr_CA',
-    /** Swiss French */
-    'fr_CH',
-    /** Western Frisian */
-    'fy',
-    /** Irish */
-    'ga',
-    /** Scottish Gaelic */
-    'gd',
-    /** Galician */
-    'gl',
-    /** Gujarati */
-    'gu',
-    /** Hausa */
-    'ha',
-    /** Hebrew */
-    'he',
-    /** Hindi */
-    'hi',
-    /** Croatian */
-    'hr',
-    /** Haitian Creole */
-    'ht',
-    /** Hungarian */
-    'hu',
-    /** Armenian */
-    'hy',
-    /** Interlingua */
-    'ia',
-    /** Indonesian */
-    'id',
-    /** Igbo */
-    'ig',
-    /** Icelandic */
-    'is',
-    /** Italian */
-    'it',
-    /** Japanese */
-    'ja',
-    /** Javanese */
-    'jv',
-    /** Georgian */
-    'ka',
-    /** Kazakh */
-    'kk',
-    /** Khmer */
-    'km',
-    /** Kannada */
-    'kn',
-    /** Korean */
-    'ko',
-    /** Kurdish */
-    'ku',
-    /** Kyrgyz */
-    'ky',
-    /** Latin */
-    'la',
-    /** Luxembourgish */
-    'lb',
-    /** Ganda */
-    'lg',
-    /** Lingala */
-    'ln',
-    /** Lao */
-    'lo',
-    /** Lithuanian */
-    'lt',
-    /** Latvian */
-    'lv',
-    /** Malagasy */
-    'mg',
-    /** Maori */
-    'mi',
-    /** Macedonian */
-    'mk',
-    /** Malayalam */
-    'ml',
-    /** Mongolian */
-    'mn',
-    /** Marathi */
-    'mr',
-    /** Malay */
-    'ms',
-    /** Maltese */
-    'mt',
-    /** Burmese */
-    'my',
-    /** Norwegian Bokmål */
-    'nb',
-    /** Nepali */
-    'ne',
-    /** Dutch */
-    'nl',
-    /** Flemish */
-    'nl_BE',
-    /** Norwegian Nynorsk */
-    'nn',
-    /** Nyanja */
-    'ny',
-    /** Oromo */
-    'om',
-    /** Odia */
-    'or',
-    /** Punjabi */
-    'pa',
-    /** Polish */
-    'pl',
-    /** Pashto */
-    'ps',
-    /** Portuguese */
-    'pt',
-    /** Brazilian Portuguese */
-    'pt_BR',
-    /** European Portuguese */
-    'pt_PT',
-    /** Quechua */
-    'qu',
-    /** Romansh */
-    'rm',
-    /** Romanian */
-    'ro',
-    /** Moldavian */
-    'ro_MD',
-    /** Russian */
-    'ru',
-    /** Kinyarwanda */
-    'rw',
-    /** Sanskrit */
-    'sa',
-    /** Sindhi */
-    'sd',
-    /** Sinhala */
-    'si',
-    /** Slovak */
-    'sk',
-    /** Slovenian */
-    'sl',
-    /** Samoan */
-    'sm',
-    /** Shona */
-    'sn',
-    /** Somali */
-    'so',
-    /** Albanian */
-    'sq',
-    /** Serbian */
-    'sr',
-    /** Southern Sotho */
-    'st',
-    /** Sundanese */
-    'su',
-    /** Swedish */
-    'sv',
-    /** Swahili */
-    'sw',
-    /** Congo Swahili */
-    'sw_CD',
-    /** Tamil */
-    'ta',
-    /** Telugu */
-    'te',
-    /** Tajik */
-    'tg',
-    /** Thai */
-    'th',
-    /** Tigrinya */
-    'ti',
-    /** Turkmen */
-    'tk',
-    /** Tongan */
-    'to',
-    /** Turkish */
-    'tr',
-    /** Tatar */
-    'tt',
-    /** Uyghur */
-    'ug',
-    /** Ukrainian */
-    'uk',
-    /** Urdu */
-    'ur',
-    /** Uzbek */
-    'uz',
-    /** Vietnamese */
-    'vi',
-    /** Wolof */
-    'wo',
-    /** Xhosa */
-    'xh',
-    /** Yiddish */
-    'yi',
-    /** Yoruba */
-    'yo',
-    /** Chinese */
-    'zh',
-    /** Simplified Chinese */
-    'zh_Hans',
-    /** Traditional Chinese */
-    'zh_Hant',
-    /** Zulu */
-    'zu',
-];