field-test-plugin.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. import { LanguageCode } from '@vendure/common/lib/generated-types';
  2. import { Collection, PaymentMethodHandler, PluginCommonModule, Product, VendurePlugin } from '@vendure/core';
  3. /**
  4. * @description
  5. * A comprehensive test payment handler that exercises every type of configurable operation argument
  6. * and UI component available in the dashboard. This handler is intended for development and testing
  7. * purposes only to validate the universal form input system.
  8. *
  9. * Tests all DefaultFormComponentId values:
  10. * - text-form-input, password-form-input, textarea-form-input
  11. * - number-form-input, currency-form-input, boolean-form-input
  12. * - select-form-input, date-form-input
  13. * - rich-text-form-input, json-editor-form-input
  14. *
  15. * Tests all ConfigArgType values:
  16. * - string, int, float, boolean, datetime, ID
  17. * - Both single values and lists
  18. * - Various UI configurations (min, max, step, options, etc.)
  19. */
  20. const comprehensiveTestPaymentHandler = new PaymentMethodHandler({
  21. code: 'comprehensive-test-payment-handler',
  22. description: [
  23. {
  24. languageCode: LanguageCode.en,
  25. value: 'Comprehensive test payment handler with all argument types and UI components',
  26. },
  27. ],
  28. args: {
  29. // === STRING ARGS ===
  30. apiKey: {
  31. type: 'string',
  32. label: [{ languageCode: LanguageCode.en, value: 'API Key' }],
  33. description: [{ languageCode: LanguageCode.en, value: 'Payment gateway API key' }],
  34. ui: { component: 'password-form-input' },
  35. required: true,
  36. },
  37. merchantId: {
  38. type: 'string',
  39. label: [{ languageCode: LanguageCode.en, value: 'Merchant ID' }],
  40. description: [{ languageCode: LanguageCode.en, value: 'Merchant identifier' }],
  41. ui: { component: 'test-input' },
  42. required: true,
  43. },
  44. color: {
  45. type: 'string',
  46. label: [{ languageCode: LanguageCode.en, value: 'Color' }],
  47. description: [{ languageCode: LanguageCode.en, value: 'Color code for this payment method' }],
  48. ui: { component: 'color-picker' },
  49. },
  50. supplierEmail: {
  51. type: 'string',
  52. label: [{ languageCode: LanguageCode.en, value: 'Supplier Email' }],
  53. pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
  54. ui: { component: 'custom-email' },
  55. },
  56. environment: {
  57. type: 'string',
  58. label: [{ languageCode: LanguageCode.en, value: 'Environment' }],
  59. description: [{ languageCode: LanguageCode.en, value: 'Payment environment' }],
  60. ui: {
  61. component: 'select-form-input',
  62. options: [
  63. { value: 'sandbox', label: [{ languageCode: LanguageCode.en, value: 'Sandbox' }] },
  64. { value: 'production', label: [{ languageCode: LanguageCode.en, value: 'Production' }] },
  65. ],
  66. },
  67. defaultValue: 'sandbox',
  68. },
  69. webhookUrl: {
  70. type: 'string',
  71. label: [{ languageCode: LanguageCode.en, value: 'Webhook URL' }],
  72. description: [{ languageCode: LanguageCode.en, value: 'Webhook endpoint URL' }],
  73. ui: { component: 'textarea-form-input' },
  74. },
  75. // === STRING LIST ARGS ===
  76. supportedCurrencies: {
  77. type: 'string',
  78. list: true,
  79. label: [{ languageCode: LanguageCode.en, value: 'Supported Currencies' }],
  80. description: [{ languageCode: LanguageCode.en, value: 'List of supported currency codes' }],
  81. },
  82. allowedCountries: {
  83. type: 'string',
  84. list: true,
  85. label: [{ languageCode: LanguageCode.en, value: 'Allowed Countries' }],
  86. description: [{ languageCode: LanguageCode.en, value: 'Countries where payment is allowed' }],
  87. ui: {
  88. component: 'select-form-input',
  89. options: [
  90. { value: 'US', label: [{ languageCode: LanguageCode.en, value: 'United States' }] },
  91. { value: 'GB', label: [{ languageCode: LanguageCode.en, value: 'United Kingdom' }] },
  92. { value: 'CA', label: [{ languageCode: LanguageCode.en, value: 'Canada' }] },
  93. { value: 'AU', label: [{ languageCode: LanguageCode.en, value: 'Australia' }] },
  94. ],
  95. },
  96. },
  97. // === INTEGER ARGS ===
  98. timeout: {
  99. type: 'int',
  100. label: [{ languageCode: LanguageCode.en, value: 'Timeout (seconds)' }],
  101. description: [{ languageCode: LanguageCode.en, value: 'Payment request timeout' }],
  102. ui: {
  103. component: 'number-form-input',
  104. min: 1,
  105. max: 300,
  106. step: 1,
  107. suffix: 's',
  108. },
  109. defaultValue: 30,
  110. },
  111. maxRetries: {
  112. type: 'int',
  113. label: [{ languageCode: LanguageCode.en, value: 'Max Retries' }],
  114. description: [{ languageCode: LanguageCode.en, value: 'Maximum retry attempts' }],
  115. ui: {
  116. component: 'number-form-input',
  117. min: 0,
  118. max: 10,
  119. step: 1,
  120. },
  121. defaultValue: 3,
  122. },
  123. // === FLOAT ARGS ===
  124. processingFee: {
  125. type: 'float',
  126. label: [{ languageCode: LanguageCode.en, value: 'Processing Fee' }],
  127. description: [{ languageCode: LanguageCode.en, value: 'Processing fee percentage' }],
  128. ui: {
  129. component: 'number-form-input',
  130. min: 0.0,
  131. max: 10.0,
  132. step: 0.01,
  133. suffix: '%',
  134. },
  135. defaultValue: 2.5,
  136. },
  137. exchangeRate: {
  138. type: 'float',
  139. label: [{ languageCode: LanguageCode.en, value: 'Exchange Rate' }],
  140. description: [{ languageCode: LanguageCode.en, value: 'Currency exchange rate' }],
  141. ui: {
  142. component: 'number-form-input',
  143. min: 0.01,
  144. step: 0.0001,
  145. },
  146. defaultValue: 1.0,
  147. },
  148. // === BOOLEAN ARGS ===
  149. enableLogging: {
  150. type: 'boolean',
  151. label: [{ languageCode: LanguageCode.en, value: 'Enable Logging' }],
  152. description: [{ languageCode: LanguageCode.en, value: 'Enable detailed logging' }],
  153. ui: { component: 'boolean-form-input' },
  154. defaultValue: false,
  155. },
  156. requireBillingAddress: {
  157. type: 'boolean',
  158. label: [{ languageCode: LanguageCode.en, value: 'Require Billing Address' }],
  159. description: [{ languageCode: LanguageCode.en, value: 'Require billing address for payments' }],
  160. ui: { component: 'boolean-form-input' },
  161. defaultValue: true,
  162. },
  163. testMode: {
  164. type: 'boolean',
  165. label: [{ languageCode: LanguageCode.en, value: 'Test Mode' }],
  166. description: [{ languageCode: LanguageCode.en, value: 'Enable test mode' }],
  167. ui: { component: 'boolean-form-input' },
  168. defaultValue: true,
  169. },
  170. // === DATETIME ARGS ===
  171. validFrom: {
  172. type: 'datetime',
  173. label: [{ languageCode: LanguageCode.en, value: 'Valid From' }],
  174. description: [{ languageCode: LanguageCode.en, value: 'Payment method valid from date' }],
  175. ui: { component: 'date-form-input' },
  176. },
  177. validUntil: {
  178. type: 'datetime',
  179. label: [{ languageCode: LanguageCode.en, value: 'Valid Until' }],
  180. description: [{ languageCode: LanguageCode.en, value: 'Payment method valid until date' }],
  181. ui: { component: 'date-form-input' },
  182. },
  183. // === ID ARGS ===
  184. partnerId: {
  185. type: 'ID',
  186. label: [{ languageCode: LanguageCode.en, value: 'Partner ID' }],
  187. description: [{ languageCode: LanguageCode.en, value: 'Payment partner identifier' }],
  188. },
  189. vendorId: {
  190. type: 'ID',
  191. label: [{ languageCode: LanguageCode.en, value: 'Vendor ID' }],
  192. description: [{ languageCode: LanguageCode.en, value: 'Payment vendor identifier' }],
  193. },
  194. // === SPECIALIZED UI COMPONENTS ===
  195. baseCurrency: {
  196. type: 'string',
  197. label: [{ languageCode: LanguageCode.en, value: 'Base Currency' }],
  198. description: [{ languageCode: LanguageCode.en, value: 'Base currency for calculations' }],
  199. ui: { component: 'currency-form-input' },
  200. defaultValue: 'USD',
  201. },
  202. termsAndConditions: {
  203. type: 'string',
  204. label: [{ languageCode: LanguageCode.en, value: 'Terms and Conditions' }],
  205. description: [{ languageCode: LanguageCode.en, value: 'Payment terms and conditions' }],
  206. ui: { component: 'rich-text-form-input' },
  207. },
  208. advancedConfig: {
  209. type: 'string',
  210. label: [{ languageCode: LanguageCode.en, value: 'Advanced Configuration' }],
  211. description: [{ languageCode: LanguageCode.en, value: 'Advanced JSON configuration' }],
  212. ui: {
  213. component: 'json-editor-form-input',
  214. height: '200px',
  215. },
  216. defaultValue: '{"webhookRetries": 3, "timeout": 30000}',
  217. },
  218. },
  219. createPayment: async (ctx, order, amount, args, metadata) => {
  220. // Simulate different payment outcomes based on metadata
  221. if (metadata.shouldDecline) {
  222. return {
  223. amount,
  224. state: 'Declined' as const,
  225. metadata: {
  226. errorMessage: 'Test decline simulation',
  227. },
  228. };
  229. } else if (metadata.shouldError) {
  230. return {
  231. amount,
  232. state: 'Error' as const,
  233. errorMessage: 'Test error simulation',
  234. metadata: {
  235. errorMessage: 'Test error simulation',
  236. },
  237. };
  238. } else {
  239. return {
  240. amount,
  241. state: args.testMode ? 'Authorized' : 'Settled',
  242. transactionId: 'test-' + Math.random().toString(36).substring(2, 7),
  243. metadata: {
  244. ...metadata,
  245. processingFee: args.processingFee,
  246. environment: args.environment,
  247. },
  248. };
  249. }
  250. },
  251. settlePayment: async (ctx, order, payment, args) => {
  252. if (payment.metadata.shouldErrorOnSettle) {
  253. return {
  254. success: false,
  255. errorMessage: 'Test settlement error simulation',
  256. };
  257. }
  258. return {
  259. success: true,
  260. metadata: {
  261. settledAt: new Date().toISOString(),
  262. processingFee: args.processingFee,
  263. },
  264. };
  265. },
  266. cancelPayment: async (ctx, order, payment) => {
  267. return {
  268. success: true,
  269. metadata: {
  270. cancellationDate: new Date().toISOString(),
  271. reason: 'Test cancellation',
  272. },
  273. };
  274. },
  275. });
  276. /**
  277. * @description
  278. * FieldTestPlugin provides comprehensive test cases for all custom field types and
  279. * configurable operation argument types supported by Vendure. This plugin is designed
  280. * specifically for development and testing purposes to validate the universal form
  281. * input system in the dashboard.
  282. *
  283. * ## Custom Fields Coverage
  284. * Tests all CustomFieldType values on the Product entity:
  285. * - string (with and without options, lists)
  286. * - localeString (translatable strings)
  287. * - text (long text fields)
  288. * - localeText (translatable long text)
  289. * - int (with min/max/step validation)
  290. * - float (with precision controls)
  291. * - boolean (single and list)
  292. * - datetime (dates and date lists)
  293. * - relation (single and multi-relation)
  294. * - struct (complex objects and lists)
  295. *
  296. * ## Configurable Operation Args Coverage
  297. * Tests all ConfigArgType values and DefaultFormComponentId components:
  298. * - All basic types: string, int, float, boolean, datetime, ID
  299. * - All UI components: text, password, textarea, number, currency, boolean,
  300. * select, date, rich-text, json-editor
  301. * - Advanced features: lists, options, validation, prefixes/suffixes
  302. *
  303. * ## UI Features Tested
  304. * - Tab organization
  305. * - Full-width layouts
  306. * - Readonly fields
  307. * - Field validation (min/max/step)
  308. * - Select options and multi-select
  309. * - List field management
  310. * - Custom UI component integration
  311. *
  312. * ## Usage
  313. * 1. Add this plugin to your dev-config.ts plugins array
  314. * 2. Navigate to any Product detail page to see custom fields
  315. * 3. Go to Settings → Payment Methods → Add "Comprehensive Test Payment Handler"
  316. * to see configurable operation arguments
  317. *
  318. * @docsCategory plugin
  319. * @since 3.4.0
  320. */
  321. @VendurePlugin({
  322. imports: [PluginCommonModule],
  323. configuration: config => {
  324. // Add comprehensive custom fields to Product entity
  325. config.customFields.Product.push(
  326. // === STRING FIELDS ===
  327. {
  328. name: 'infoUrl',
  329. type: 'string',
  330. label: [{ languageCode: LanguageCode.en, value: 'Info URL' }],
  331. description: [{ languageCode: LanguageCode.en, value: 'Product information URL' }],
  332. },
  333. {
  334. name: 'customSku',
  335. type: 'string',
  336. label: [{ languageCode: LanguageCode.en, value: 'Custom SKU' }],
  337. description: [{ languageCode: LanguageCode.en, value: 'Custom SKU for this product' }],
  338. readonly: true,
  339. },
  340. {
  341. name: 'supplierEmail',
  342. type: 'string',
  343. label: [{ languageCode: LanguageCode.en, value: 'Supplier Email' }],
  344. pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
  345. ui: { component: 'custom-email' },
  346. },
  347. {
  348. name: 'RRP',
  349. type: 'int',
  350. label: [{ languageCode: LanguageCode.en, value: 'RRP' }],
  351. ui: { component: 'multi-currency-input' },
  352. },
  353. {
  354. name: 'simpleTags',
  355. type: 'string',
  356. label: [{ languageCode: LanguageCode.en, value: 'Product tags' }],
  357. ui: { component: 'tags-input' },
  358. },
  359. {
  360. name: 'color',
  361. type: 'string',
  362. pattern: '^#[A-Fa-f0-9]{6}$',
  363. label: [{ languageCode: LanguageCode.en, value: 'Color' }],
  364. description: [{ languageCode: LanguageCode.en, value: 'Main color for this product' }],
  365. ui: {
  366. component: 'color-picker',
  367. },
  368. },
  369. {
  370. name: 'category',
  371. type: 'string',
  372. list: false,
  373. label: [{ languageCode: LanguageCode.en, value: 'Category' }],
  374. description: [{ languageCode: LanguageCode.en, value: 'Product category selection' }],
  375. options: [
  376. {
  377. value: 'electronics',
  378. label: [{ languageCode: LanguageCode.en, value: 'Electronics' }],
  379. },
  380. { value: 'clothing', label: [{ languageCode: LanguageCode.en, value: 'Clothing' }] },
  381. { value: 'books', label: [{ languageCode: LanguageCode.en, value: 'Books' }] },
  382. { value: 'home', label: [{ languageCode: LanguageCode.en, value: 'Home & Garden' }] },
  383. ],
  384. },
  385. {
  386. name: 'tags',
  387. type: 'string',
  388. list: true,
  389. label: [{ languageCode: LanguageCode.en, value: 'Tags' }],
  390. description: [{ languageCode: LanguageCode.en, value: 'Product tags (list)' }],
  391. },
  392. {
  393. name: 'features',
  394. type: 'string',
  395. list: true,
  396. label: [{ languageCode: LanguageCode.en, value: 'Key Features' }],
  397. description: [{ languageCode: LanguageCode.en, value: 'List of product features' }],
  398. options: [
  399. { value: 'wireless', label: [{ languageCode: LanguageCode.en, value: 'Wireless' }] },
  400. { value: 'waterproof', label: [{ languageCode: LanguageCode.en, value: 'Waterproof' }] },
  401. {
  402. value: 'rechargeable',
  403. label: [{ languageCode: LanguageCode.en, value: 'Rechargeable' }],
  404. },
  405. { value: 'portable', label: [{ languageCode: LanguageCode.en, value: 'Portable' }] },
  406. ],
  407. },
  408. // === LOCALE STRING FIELDS ===
  409. {
  410. name: 'shortName',
  411. type: 'localeString',
  412. label: [{ languageCode: LanguageCode.en, value: 'Short Name' }],
  413. description: [{ languageCode: LanguageCode.en, value: 'Short product name (translatable)' }],
  414. },
  415. {
  416. name: 'seoTitle',
  417. type: 'localeString',
  418. label: [{ languageCode: LanguageCode.en, value: 'SEO Title' }],
  419. description: [{ languageCode: LanguageCode.en, value: 'SEO page title (translatable)' }],
  420. ui: { tab: 'SEO' },
  421. },
  422. // === TEXT FIELDS ===
  423. {
  424. name: 'specifications',
  425. type: 'text',
  426. label: [{ languageCode: LanguageCode.en, value: 'Specifications' }],
  427. description: [{ languageCode: LanguageCode.en, value: 'Product specifications (long text)' }],
  428. ui: { fullWidth: false, component: 'test-input' },
  429. },
  430. {
  431. name: 'warrantyInfo',
  432. type: 'localeText',
  433. label: [{ languageCode: LanguageCode.en, value: 'Warranty Information' }],
  434. description: [
  435. { languageCode: LanguageCode.en, value: 'Warranty details (translatable long text)' },
  436. ],
  437. ui: { fullWidth: true, tab: 'Details' },
  438. },
  439. // === BOOLEAN FIELDS ===
  440. {
  441. name: 'downloadable',
  442. type: 'boolean',
  443. label: [{ languageCode: LanguageCode.en, value: 'Downloadable' }],
  444. description: [{ languageCode: LanguageCode.en, value: 'Is this a downloadable product?' }],
  445. },
  446. {
  447. name: 'featured',
  448. type: 'boolean',
  449. label: [{ languageCode: LanguageCode.en, value: 'Featured Product' }],
  450. description: [{ languageCode: LanguageCode.en, value: 'Show on homepage' }],
  451. },
  452. {
  453. name: 'exclusiveOffers',
  454. type: 'boolean',
  455. list: true,
  456. label: [{ languageCode: LanguageCode.en, value: 'Exclusive Offers' }],
  457. description: [{ languageCode: LanguageCode.en, value: 'Multiple boolean values' }],
  458. },
  459. // === INTEGER FIELDS ===
  460. {
  461. name: 'weight',
  462. type: 'int',
  463. label: [{ languageCode: LanguageCode.en, value: 'Weight (grams)' }],
  464. description: [{ languageCode: LanguageCode.en, value: 'Product weight in grams' }],
  465. min: 0,
  466. max: 50000,
  467. step: 10,
  468. },
  469. {
  470. name: 'priority',
  471. type: 'int',
  472. label: [{ languageCode: LanguageCode.en, value: 'Priority' }],
  473. description: [{ languageCode: LanguageCode.en, value: 'Display priority (1-10)' }],
  474. min: 1,
  475. max: 10,
  476. step: 1,
  477. },
  478. {
  479. name: 'dimensions',
  480. type: 'int',
  481. list: true,
  482. label: [{ languageCode: LanguageCode.en, value: 'Dimensions (L×W×H)' }],
  483. description: [{ languageCode: LanguageCode.en, value: 'Product dimensions in cm' }],
  484. min: 0,
  485. max: 1000,
  486. },
  487. // === FLOAT FIELDS ===
  488. {
  489. name: 'rating',
  490. type: 'float',
  491. label: [{ languageCode: LanguageCode.en, value: 'Average Rating' }],
  492. description: [{ languageCode: LanguageCode.en, value: 'Average customer rating' }],
  493. min: 0.0,
  494. max: 5.0,
  495. step: 0.1,
  496. readonly: true,
  497. },
  498. {
  499. name: 'temperature',
  500. type: 'float',
  501. label: [{ languageCode: LanguageCode.en, value: 'Operating Temperature' }],
  502. description: [{ languageCode: LanguageCode.en, value: 'Operating temperature range' }],
  503. min: -40.0,
  504. max: 85.0,
  505. step: 0.5,
  506. },
  507. {
  508. name: 'measurements',
  509. type: 'float',
  510. list: true,
  511. label: [{ languageCode: LanguageCode.en, value: 'Measurements' }],
  512. description: [{ languageCode: LanguageCode.en, value: 'Precise measurements list' }],
  513. step: 0.01,
  514. },
  515. // === DATETIME FIELDS ===
  516. {
  517. name: 'lastUpdated',
  518. type: 'datetime',
  519. label: [{ languageCode: LanguageCode.en, value: 'Last Updated' }],
  520. description: [{ languageCode: LanguageCode.en, value: 'When product was last updated' }],
  521. readonly: true,
  522. },
  523. {
  524. name: 'releaseDate',
  525. type: 'datetime',
  526. label: [{ languageCode: LanguageCode.en, value: 'Release Date' }],
  527. description: [{ languageCode: LanguageCode.en, value: 'Product release date' }],
  528. },
  529. {
  530. name: 'availabilityDates',
  531. type: 'datetime',
  532. list: true,
  533. label: [{ languageCode: LanguageCode.en, value: 'Availability Dates' }],
  534. description: [{ languageCode: LanguageCode.en, value: 'Special availability dates' }],
  535. },
  536. // === RELATION FIELDS ===
  537. {
  538. name: 'brand',
  539. type: 'relation',
  540. entity: Collection,
  541. label: [{ languageCode: LanguageCode.en, value: 'Brand' }],
  542. description: [
  543. { languageCode: LanguageCode.en, value: 'Product brand (collection relation)' },
  544. ],
  545. },
  546. {
  547. name: 'relatedProducts',
  548. type: 'relation',
  549. entity: Product,
  550. list: true,
  551. label: [{ languageCode: LanguageCode.en, value: 'Related Products' }],
  552. description: [{ languageCode: LanguageCode.en, value: 'List of related products' }],
  553. },
  554. {
  555. name: 'manufacturer',
  556. type: 'relation',
  557. entity: Collection,
  558. label: [{ languageCode: LanguageCode.en, value: 'Manufacturer' }],
  559. description: [{ languageCode: LanguageCode.en, value: 'Product manufacturer' }],
  560. ui: { tab: 'Details' },
  561. },
  562. // === STRUCT FIELDS ===
  563. {
  564. name: 'productSpecs',
  565. type: 'struct',
  566. label: [{ languageCode: LanguageCode.en, value: 'Product Specifications' }],
  567. description: [{ languageCode: LanguageCode.en, value: 'Structured product specifications' }],
  568. ui: { fullWidth: true, tab: 'Specifications' },
  569. fields: [
  570. { name: 'cpu', type: 'string' as const },
  571. { name: 'memory', type: 'int' as const },
  572. { name: 'storage', type: 'int' as const },
  573. { name: 'display', type: 'string' as const },
  574. ],
  575. },
  576. {
  577. name: 'variations',
  578. type: 'struct',
  579. list: true,
  580. label: [{ languageCode: LanguageCode.en, value: 'Product Variations' }],
  581. description: [
  582. { languageCode: LanguageCode.en, value: 'List of product variant specifications' },
  583. ],
  584. ui: { fullWidth: true, tab: 'Variants' },
  585. fields: [
  586. { name: 'color', type: 'string' as const },
  587. { name: 'size', type: 'string' as const },
  588. { name: 'price', type: 'float' as const },
  589. { name: 'inStock', type: 'boolean' as const },
  590. ],
  591. },
  592. // === FIELDS WITH CUSTOM UI COMPONENTS (if available) ===
  593. {
  594. name: 'customData',
  595. type: 'string',
  596. label: [{ languageCode: LanguageCode.en, value: 'Custom Data' }],
  597. description: [{ languageCode: LanguageCode.en, value: 'Field with custom UI component' }],
  598. ui: {
  599. component: 'custom-text-input', // This would need to be registered
  600. tab: 'Advanced',
  601. },
  602. },
  603. // === FIELDS WITH TABS ===
  604. {
  605. name: 'seoDescription',
  606. type: 'text',
  607. label: [{ languageCode: LanguageCode.en, value: 'SEO Description' }],
  608. description: [{ languageCode: LanguageCode.en, value: 'SEO meta description' }],
  609. ui: { tab: 'SEO', fullWidth: true },
  610. },
  611. {
  612. name: 'seoKeywords',
  613. type: 'string',
  614. list: true,
  615. label: [{ languageCode: LanguageCode.en, value: 'SEO Keywords' }],
  616. description: [{ languageCode: LanguageCode.en, value: 'SEO keywords' }],
  617. ui: { tab: 'SEO' },
  618. },
  619. {
  620. name: 'technicalNotes',
  621. type: 'text',
  622. label: [{ languageCode: LanguageCode.en, value: 'Technical Notes' }],
  623. description: [{ languageCode: LanguageCode.en, value: 'Internal technical notes' }],
  624. ui: { tab: 'Internal', fullWidth: true },
  625. },
  626. {
  627. name: 'internalCode',
  628. type: 'string',
  629. label: [{ languageCode: LanguageCode.en, value: 'Internal Code' }],
  630. description: [{ languageCode: LanguageCode.en, value: 'Internal tracking code' }],
  631. ui: { tab: 'Internal' },
  632. },
  633. );
  634. // Add comprehensive test payment handler
  635. config.paymentOptions.paymentMethodHandlers.push(comprehensiveTestPaymentHandler);
  636. return config;
  637. },
  638. dashboard: './dashboard/index.tsx',
  639. })
  640. export class FieldTestPlugin {}