@configuratorware/configurator-frontendgui 1.57.3 → 1.57.5

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.
@@ -30,6 +30,14 @@ var _SvgFixers = require("../Modules/Designer/Utils/SvgFixers");
30
30
  var _excluded = ["code"];
31
31
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t2 in e) "default" !== _t2 && {}.hasOwnProperty.call(e, _t2) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t2)) && (i.get || i.set) ? o(f, _t2, i) : f[_t2] = e[_t2]); return f; })(e, t); }
32
32
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
33
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
34
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
35
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
36
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
37
+ function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
38
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
39
+ function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
40
+ function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
33
41
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
34
42
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
35
43
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -559,6 +567,9 @@ var ConfiguratorService = exports["default"] = /*#__PURE__*/function (_AbstractC
559
567
  fullSvgs = _context6.sent;
560
568
  mediaData.printDesignData = (0, _SvgFixers.preparePrintDesignData)(fullSvgs);
561
569
  usedAreas = Object.keys(fullSvgs);
570
+ _context6.next = 15;
571
+ return this.captureBulkNameDesignData(configuration, mediaData, fontMapping);
572
+ case 15:
562
573
  designArea = _ServiceLocator.Services.store.state.designArea;
563
574
  filteredDesignAreas = designArea.filteredDesignAreas;
564
575
  usedViews = (0, _Selectors2.getCameraViewListForDesignAreaList)(filteredDesignAreas, usedAreas);
@@ -595,8 +606,8 @@ var ConfiguratorService = exports["default"] = /*#__PURE__*/function (_AbstractC
595
606
  }
596
607
  designViewList = (0, _Selectors2.getDesignViewListForDesignAreas)(_ServiceLocator.Services.store.state);
597
608
  mediaData.screenshots = {};
598
- _context6.prev = 23;
599
- _context6.next = 26;
609
+ _context6.prev = 25;
610
+ _context6.next = 28;
600
611
  return _ServiceLocator.Services.visualization.getScreenshots({
601
612
  areaList: usedAreas,
602
613
  // used in 2D visualization
@@ -605,7 +616,7 @@ var ConfiguratorService = exports["default"] = /*#__PURE__*/function (_AbstractC
605
616
  variantList: selectedVariants,
606
617
  designViewList: designViewList // used in 2D visualization
607
618
  });
608
- case 26:
619
+ case 28:
609
620
  screenshots = _context6.sent;
610
621
  if (screenshots) {
611
622
  // make sure the order is kept on the server side
@@ -631,69 +642,173 @@ var ConfiguratorService = exports["default"] = /*#__PURE__*/function (_AbstractC
631
642
  }
632
643
  }
633
644
  mediaData.screenshots = screenshots;
634
- _context6.next = 34;
645
+ _context6.next = 36;
635
646
  break;
636
- case 31:
637
- _context6.prev = 31;
638
- _context6.t0 = _context6["catch"](23);
647
+ case 33:
648
+ _context6.prev = 33;
649
+ _context6.t0 = _context6["catch"](25);
639
650
  // do not save in case of an error while generating screenshots
640
651
  // eslint-disable-next-line no-console
641
652
  console.error('Error occurred while generating screenshots:', _context6.t0);
642
- case 34:
653
+ case 36:
643
654
  memo.restore();
644
655
  if (_typeof(configuration) === 'object') {
645
656
  Object.assign(configuration, mediaData);
646
657
  }
647
658
  restorePlaceholderVisibility();
648
659
  return _context6.abrupt("return", mediaData);
649
- case 38:
660
+ case 40:
650
661
  case "end":
651
662
  return _context6.stop();
652
663
  }
653
- }, _callee6, this, [[23, 31]]);
664
+ }, _callee6, this, [[25, 33]]);
654
665
  }));
655
666
  function createMediaDataForConfiguration(_x7) {
656
667
  return _createMediaDataForConfiguration.apply(this, arguments);
657
668
  }
658
669
  return createMediaDataForConfiguration;
659
670
  }()
671
+ /**
672
+ * Opt-in (getConf('esigner.bulkNamesPerNameDesignFiles')). When the configuration uses bulk names,
673
+ * captures one name-substituted set of design SVGs per unique bulk name and adds them under
674
+ * `{area}={encodeURIComponent(name)}` keys, alongside the existing placeholder entries. Design,
675
+ * placement and font are unchanged - only the bulk-name text content is swapped
676
+ * (via withBulkNameApplied). No-op when the flag is off or there are no bulk names.
677
+ */
678
+ }, {
679
+ key: "captureBulkNameDesignData",
680
+ value: function () {
681
+ var _captureBulkNameDesignData = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee8(configuration, mediaData, fontMapping) {
682
+ var bulkNamesByVariant, uniqueNames, _iterator5, _step5, _loop;
683
+ return _regeneratorRuntime().wrap(function _callee8$(_context9) {
684
+ while (1) switch (_context9.prev = _context9.next) {
685
+ case 0:
686
+ if ((0, _configuration2.getConf)('designer.bulkNamesPerNameDesignFiles', false)) {
687
+ _context9.next = 2;
688
+ break;
689
+ }
690
+ return _context9.abrupt("return");
691
+ case 2:
692
+ bulkNamesByVariant = configuration && configuration.customdata && configuration.customdata.bulkNames || {};
693
+ uniqueNames = _toConsumableArray(new Set(Object.values(bulkNamesByVariant).flat().filter(function (name) {
694
+ return typeof name === 'string' && name.trim() !== '';
695
+ })));
696
+ _iterator5 = _createForOfIteratorHelper(uniqueNames);
697
+ _context9.prev = 5;
698
+ _loop = /*#__PURE__*/_regeneratorRuntime().mark(function _loop() {
699
+ var name;
700
+ return _regeneratorRuntime().wrap(function _loop$(_context8) {
701
+ while (1) switch (_context8.prev = _context8.next) {
702
+ case 0:
703
+ name = _step5.value;
704
+ _context8.next = 3;
705
+ return _ServiceLocator.Services.designData.withBulkNameApplied(name, /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee7() {
706
+ var printSvgs, elementSvgs, key, _i2, _Object$entries, _Object$entries$_i, area, svg, _i3, _Object$entries2, _Object$entries2$_i, _area, svgArray;
707
+ return _regeneratorRuntime().wrap(function _callee7$(_context7) {
708
+ while (1) switch (_context7.prev = _context7.next) {
709
+ case 0:
710
+ _context7.t0 = _SvgFixers.preparePrintDesignData;
711
+ _context7.next = 3;
712
+ return _ServiceLocator.Services.designData.getScreenshots(fontMapping);
713
+ case 3:
714
+ _context7.t1 = _context7.sent;
715
+ printSvgs = (0, _context7.t0)(_context7.t1);
716
+ _context7.t2 = _SvgFixers.prepareDesignDataSnapshots;
717
+ _context7.next = 8;
718
+ return _ServiceLocator.Services.designData.getScreenshotsOfElements(fontMapping);
719
+ case 8:
720
+ _context7.t3 = _context7.sent;
721
+ elementSvgs = (0, _context7.t2)(_context7.t3);
722
+ key = encodeURIComponent(name);
723
+ for (_i2 = 0, _Object$entries = Object.entries(printSvgs); _i2 < _Object$entries.length; _i2++) {
724
+ _Object$entries$_i = _slicedToArray(_Object$entries[_i2], 2), area = _Object$entries$_i[0], svg = _Object$entries$_i[1];
725
+ mediaData.printDesignData["".concat(area, "=").concat(key)] = svg;
726
+ }
727
+ for (_i3 = 0, _Object$entries2 = Object.entries(elementSvgs); _i3 < _Object$entries2.length; _i3++) {
728
+ _Object$entries2$_i = _slicedToArray(_Object$entries2[_i3], 2), _area = _Object$entries2$_i[0], svgArray = _Object$entries2$_i[1];
729
+ mediaData.designDataSnapshots["".concat(_area, "=").concat(key)] = svgArray;
730
+ }
731
+ case 13:
732
+ case "end":
733
+ return _context7.stop();
734
+ }
735
+ }, _callee7);
736
+ })));
737
+ case 3:
738
+ case "end":
739
+ return _context8.stop();
740
+ }
741
+ }, _loop);
742
+ });
743
+ _iterator5.s();
744
+ case 8:
745
+ if ((_step5 = _iterator5.n()).done) {
746
+ _context9.next = 12;
747
+ break;
748
+ }
749
+ return _context9.delegateYield(_loop(), "t0", 10);
750
+ case 10:
751
+ _context9.next = 8;
752
+ break;
753
+ case 12:
754
+ _context9.next = 17;
755
+ break;
756
+ case 14:
757
+ _context9.prev = 14;
758
+ _context9.t1 = _context9["catch"](5);
759
+ _iterator5.e(_context9.t1);
760
+ case 17:
761
+ _context9.prev = 17;
762
+ _iterator5.f();
763
+ return _context9.finish(17);
764
+ case 20:
765
+ case "end":
766
+ return _context9.stop();
767
+ }
768
+ }, _callee8, null, [[5, 14, 17, 20]]);
769
+ }));
770
+ function captureBulkNameDesignData(_x8, _x9, _x0) {
771
+ return _captureBulkNameDesignData.apply(this, arguments);
772
+ }
773
+ return captureBulkNameDesignData;
774
+ }()
660
775
  }, {
661
776
  key: "captureAndSaveConfiguration",
662
777
  value: function () {
663
- var _captureAndSaveConfiguration = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee7(saveType) {
778
+ var _captureAndSaveConfiguration = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee9(saveType) {
664
779
  var extraData,
665
780
  configuration,
666
- _args7 = arguments;
667
- return _regeneratorRuntime().wrap(function _callee7$(_context7) {
668
- while (1) switch (_context7.prev = _context7.next) {
781
+ _args0 = arguments;
782
+ return _regeneratorRuntime().wrap(function _callee9$(_context0) {
783
+ while (1) switch (_context0.prev = _context0.next) {
669
784
  case 0:
670
- extraData = _args7.length > 1 && _args7[1] !== undefined ? _args7[1] : {};
671
- _context7.prev = 1;
672
- _context7.next = 4;
785
+ extraData = _args0.length > 1 && _args0[1] !== undefined ? _args0[1] : {};
786
+ _context0.prev = 1;
787
+ _context0.next = 4;
673
788
  return this.editNameAsync(saveType);
674
789
  case 4:
675
- _context7.next = 10;
790
+ _context0.next = 10;
676
791
  break;
677
792
  case 6:
678
- _context7.prev = 6;
679
- _context7.t0 = _context7["catch"](1);
793
+ _context0.prev = 6;
794
+ _context0.t0 = _context0["catch"](1);
680
795
  // do not save if the name is not entered / confirmed
681
796
  // eslint-disable-next-line no-console
682
797
  console.error('Custom name editor not confirmed!');
683
- return _context7.abrupt("return");
798
+ return _context0.abrupt("return");
684
799
  case 10:
685
800
  configuration = this.prepareConfigurationForSave(false, extraData);
686
- _context7.next = 13;
801
+ _context0.next = 13;
687
802
  return this.createMediaDataForConfiguration(configuration);
688
803
  case 13:
689
- return _context7.abrupt("return", this.saveConfiguration(configuration, saveType));
804
+ return _context0.abrupt("return", this.saveConfiguration(configuration, saveType));
690
805
  case 14:
691
806
  case "end":
692
- return _context7.stop();
807
+ return _context0.stop();
693
808
  }
694
- }, _callee7, this, [[1, 6]]);
809
+ }, _callee9, this, [[1, 6]]);
695
810
  }));
696
- function captureAndSaveConfiguration(_x8) {
811
+ function captureAndSaveConfiguration(_x1) {
697
812
  return _captureAndSaveConfiguration.apply(this, arguments);
698
813
  }
699
814
  return captureAndSaveConfiguration;
@@ -714,17 +829,17 @@ var ConfiguratorService = exports["default"] = /*#__PURE__*/function (_AbstractC
714
829
  }, {
715
830
  key: "saveAndReloadConfiguration",
716
831
  value: function () {
717
- var _saveAndReloadConfiguration = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee8(saveType) {
832
+ var _saveAndReloadConfiguration = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee0(saveType) {
718
833
  var tmpConfiguration, _configuration, _this$state, selectedoptionclassification, configuration, loadIdentifier, itemIdentifier;
719
- return _regeneratorRuntime().wrap(function _callee8$(_context8) {
720
- while (1) switch (_context8.prev = _context8.next) {
834
+ return _regeneratorRuntime().wrap(function _callee0$(_context1) {
835
+ while (1) switch (_context1.prev = _context1.next) {
721
836
  case 0:
722
837
  if (!this.state.configuration.item.identifier) {
723
- _context8.next = 6;
838
+ _context1.next = 6;
724
839
  break;
725
840
  }
726
841
  tmpConfiguration = (0, _omit["default"])(this.state.configuration, ['code', 'date_created']);
727
- _context8.next = 4;
842
+ _context1.next = 4;
728
843
  return this.saveConfiguration(tmpConfiguration, saveType);
729
844
  case 4:
730
845
  _configuration = this.state.configuration;
@@ -733,10 +848,10 @@ var ConfiguratorService = exports["default"] = /*#__PURE__*/function (_AbstractC
733
848
  _this$state = this.state, selectedoptionclassification = _this$state.selectedoptionclassification, configuration = _this$state.configuration, loadIdentifier = _this$state.loadIdentifier;
734
849
  itemIdentifier = configuration.item.identifier || loadIdentifier; // item identifier is required for the following actions
735
850
  if (itemIdentifier) {
736
- _context8.next = 10;
851
+ _context1.next = 10;
737
852
  break;
738
853
  }
739
- return _context8.abrupt("return");
854
+ return _context1.abrupt("return");
740
855
  case 10:
741
856
  // reload inspirations
742
857
  if ((0, _configuration2.getConf)('loadInspirations')) {
@@ -752,11 +867,11 @@ var ConfiguratorService = exports["default"] = /*#__PURE__*/function (_AbstractC
752
867
  }
753
868
  case 13:
754
869
  case "end":
755
- return _context8.stop();
870
+ return _context1.stop();
756
871
  }
757
- }, _callee8, this);
872
+ }, _callee0, this);
758
873
  }));
759
- function saveAndReloadConfiguration(_x9) {
874
+ function saveAndReloadConfiguration(_x10) {
760
875
  return _saveAndReloadConfiguration.apply(this, arguments);
761
876
  }
762
877
  return saveAndReloadConfiguration;
@@ -818,8 +933,8 @@ var ConfiguratorService = exports["default"] = /*#__PURE__*/function (_AbstractC
818
933
  key: "attachCodeSnippets",
819
934
  value: function attachCodeSnippets(codeSnippets) {
820
935
  if (codeSnippets.data.length > 0) {
821
- codeSnippets.data.forEach(function (_ref5) {
822
- var code = _ref5.code;
936
+ codeSnippets.data.forEach(function (_ref6) {
937
+ var code = _ref6.code;
823
938
  var fragment = document.createRange().createContextualFragment(code);
824
939
  while (fragment.children.length > 0) {
825
940
  document.head.insertBefore(fragment.children[0], document.head.firstElementChild);
@@ -207,7 +207,9 @@ var applicationConfiguration = {
207
207
  // the default max number of colors in the colorizing feature
208
208
  allowedImageUploadFormats: 'jpg,jpeg,svg,png,pdf,bmp,tif,tiff,eps,ai,gif',
209
209
  // the image formats users can upload to designareas / designer items
210
- includeMetricContentSize: false // set true to include the metric content size in the design data
210
+ includeMetricContentSize: false,
211
+ // set true to include the metric content size in the design data
212
+ bulkNamesPerNameDesignFiles: false // set true to generate individual production svgs (-> production pdf in zip download) for each bulk name
211
213
  },
212
214
 
213
215
  vectorizeImageQuality: 45000,
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@configuratorware/configurator-frontendgui",
3
- "version": "1.57.3",
3
+ "version": "1.57.5",
4
4
  "license": "UNLICENSED",
5
5
  "private": false,
6
6
  "main": "./index.js",
7
7
  "dependencies": {
8
8
  "@babel/polyfill": "^7.12.1",
9
- "@configuratorware/scripts": "1.57.3",
9
+ "@configuratorware/scripts": "1.57.5",
10
10
  "@material-ui/core": "^4.12.4",
11
11
  "@material-ui/icons": "^4.11.3",
12
12
  "@material-ui/lab": "^4.0.0-alpha.61",
@@ -36,8 +36,8 @@
36
36
  "react-router-dom": "^5.3.4",
37
37
  "react-swipeable": "^5.5.1",
38
38
  "react-zoom-pan-pinch": "^2.1.3",
39
- "redhotmagma-graphics-editor": "1.57.3",
40
- "redhotmagma-visualization": "1.57.3",
39
+ "redhotmagma-graphics-editor": "1.57.5",
40
+ "redhotmagma-visualization": "1.57.5",
41
41
  "redux": "^4.1.0",
42
42
  "redux-logger": "^3.0.6",
43
43
  "redux-persist": "^5.10.0",
@@ -382,6 +382,8 @@ export default class ConfiguratorService extends AbstractConfiguratorService {
382
382
  mediaData.printDesignData = preparePrintDesignData(fullSvgs);
383
383
  const usedAreas = Object.keys(fullSvgs);
384
384
 
385
+ await this.captureBulkNameDesignData(configuration, mediaData, fontMapping);
386
+
385
387
  const { designArea } = Services.store.state;
386
388
  const { filteredDesignAreas } = designArea;
387
389
  const usedViews = getCameraViewListForDesignAreaList(filteredDesignAreas, usedAreas);
@@ -456,6 +458,48 @@ export default class ConfiguratorService extends AbstractConfiguratorService {
456
458
  return mediaData;
457
459
  }
458
460
 
461
+ /**
462
+ * Opt-in (getConf('esigner.bulkNamesPerNameDesignFiles')). When the configuration uses bulk names,
463
+ * captures one name-substituted set of design SVGs per unique bulk name and adds them under
464
+ * `{area}={encodeURIComponent(name)}` keys, alongside the existing placeholder entries. Design,
465
+ * placement and font are unchanged - only the bulk-name text content is swapped
466
+ * (via withBulkNameApplied). No-op when the flag is off or there are no bulk names.
467
+ */
468
+ async captureBulkNameDesignData(configuration, mediaData, fontMapping) {
469
+ if (!getConf('designer.bulkNamesPerNameDesignFiles', false)) {
470
+ return;
471
+ }
472
+
473
+ const bulkNamesByVariant =
474
+ (configuration && configuration.customdata && configuration.customdata.bulkNames) || {};
475
+ const uniqueNames = [
476
+ ...new Set(
477
+ Object.values(bulkNamesByVariant)
478
+ .flat()
479
+ .filter(name => typeof name === 'string' && name.trim() !== '')
480
+ ),
481
+ ];
482
+
483
+ for (const name of uniqueNames) {
484
+ await Services.designData.withBulkNameApplied(name, async () => {
485
+ const printSvgs = preparePrintDesignData(
486
+ await Services.designData.getScreenshots(fontMapping)
487
+ );
488
+ const elementSvgs = prepareDesignDataSnapshots(
489
+ await Services.designData.getScreenshotsOfElements(fontMapping)
490
+ );
491
+ const key = encodeURIComponent(name);
492
+
493
+ for (const [area, svg] of Object.entries(printSvgs)) {
494
+ mediaData.printDesignData[`${area}=${key}`] = svg;
495
+ }
496
+ for (const [area, svgArray] of Object.entries(elementSvgs)) {
497
+ mediaData.designDataSnapshots[`${area}=${key}`] = svgArray;
498
+ }
499
+ });
500
+ }
501
+ }
502
+
459
503
  async captureAndSaveConfiguration(saveType, extraData = {}) {
460
504
  try {
461
505
  await this.editNameAsync(saveType);
@@ -491,4 +491,104 @@ describe('Services/ConfiguratorService', () => {
491
491
  }
492
492
  );
493
493
  });
494
+
495
+ describe('captureBulkNameDesignData', () => {
496
+ const provideDesignDataServiceMock = () => {
497
+ let appliedName = null;
498
+ const designDataSrvc = new (class DesignDataService {
499
+ static serviceName = 'DesignDataService';
500
+ withBulkNameApplied = jest.fn(async (name, callback) => {
501
+ appliedName = name;
502
+ const result = await callback();
503
+ appliedName = null;
504
+ return result;
505
+ });
506
+ getScreenshots = jest.fn(async () => ({
507
+ chest: `print:${appliedName}`,
508
+ back: `print:${appliedName}`,
509
+ }));
510
+ getScreenshotsOfElements = jest.fn(async () => ({
511
+ chest: [`el:${appliedName}`],
512
+ back: [`el:${appliedName}`],
513
+ }));
514
+ })();
515
+ ServiceLocator.provide(designDataSrvc);
516
+ return designDataSrvc;
517
+ };
518
+
519
+ beforeEach(() => {
520
+ ServiceLocator.provide(new StoreService());
521
+ });
522
+
523
+ it('adds one name-scoped entry per unique bulk name to both maps when the flag is on', async () => {
524
+ setConf('designer.bulkNamesPerNameDesignFiles', true);
525
+ const designDataSrvc = provideDesignDataServiceMock();
526
+
527
+ const configuration = {
528
+ customdata: { bulkNames: { variantA: ['Fred', 'Adi', 'Fred'], variantB: ['Adi'] } },
529
+ };
530
+ const mediaData = { printDesignData: {}, designDataSnapshots: {} };
531
+
532
+ const configuratorSrvc = new ConfiguratorService();
533
+ await configuratorSrvc.captureBulkNameDesignData(configuration, mediaData, {});
534
+
535
+ // union-deduped across variants -> Fred, Adi only
536
+ expect(designDataSrvc.withBulkNameApplied).toHaveBeenCalledTimes(2);
537
+ expect(mediaData.printDesignData).toEqual({
538
+ 'chest=Fred': 'print:Fred',
539
+ 'back=Fred': 'print:Fred',
540
+ 'chest=Adi': 'print:Adi',
541
+ 'back=Adi': 'print:Adi',
542
+ });
543
+ expect(mediaData.designDataSnapshots).toEqual({
544
+ 'chest=Fred': ['el:Fred'],
545
+ 'back=Fred': ['el:Fred'],
546
+ 'chest=Adi': ['el:Adi'],
547
+ 'back=Adi': ['el:Adi'],
548
+ });
549
+ });
550
+
551
+ it('encodes special characters in the name-scoped keys', async () => {
552
+ setConf('designer.bulkNamesPerNameDesignFiles', true);
553
+ provideDesignDataServiceMock();
554
+
555
+ const configuration = { customdata: { bulkNames: { v: ['von Trapp'] } } };
556
+ const mediaData = { printDesignData: {}, designDataSnapshots: {} };
557
+
558
+ const configuratorSrvc = new ConfiguratorService();
559
+ await configuratorSrvc.captureBulkNameDesignData(configuration, mediaData, {});
560
+
561
+ expect(Object.keys(mediaData.printDesignData)).toEqual(['chest=von%20Trapp', 'back=von%20Trapp']);
562
+ });
563
+
564
+ it('is a no-op when the flag is off', async () => {
565
+ setConf('designer.bulkNamesPerNameDesignFiles', false);
566
+ const designDataSrvc = provideDesignDataServiceMock();
567
+
568
+ const configuration = { customdata: { bulkNames: { variantA: ['Fred'] } } };
569
+ const mediaData = { printDesignData: {}, designDataSnapshots: {} };
570
+
571
+ const configuratorSrvc = new ConfiguratorService();
572
+ await configuratorSrvc.captureBulkNameDesignData(configuration, mediaData, {});
573
+
574
+ expect(designDataSrvc.withBulkNameApplied).not.toHaveBeenCalled();
575
+ expect(mediaData.printDesignData).toEqual({});
576
+ expect(mediaData.designDataSnapshots).toEqual({});
577
+ });
578
+
579
+ it('is a no-op when there are no bulk names', async () => {
580
+ setConf('designer.bulkNamesPerNameDesignFiles', true);
581
+ const designDataSrvc = provideDesignDataServiceMock();
582
+
583
+ const configuration = { customdata: { bulkNames: {} } };
584
+ const mediaData = { printDesignData: {}, designDataSnapshots: {} };
585
+
586
+ const configuratorSrvc = new ConfiguratorService();
587
+ await configuratorSrvc.captureBulkNameDesignData(configuration, mediaData, {});
588
+
589
+ expect(designDataSrvc.withBulkNameApplied).not.toHaveBeenCalled();
590
+ expect(mediaData.printDesignData).toEqual({});
591
+ expect(mediaData.designDataSnapshots).toEqual({});
592
+ });
593
+ });
494
594
  });
@@ -215,6 +215,7 @@ let applicationConfiguration = {
215
215
  maxColorAmount: 10, // the default max number of colors in the colorizing feature
216
216
  allowedImageUploadFormats: 'jpg,jpeg,svg,png,pdf,bmp,tif,tiff,eps,ai,gif', // the image formats users can upload to designareas / designer items
217
217
  includeMetricContentSize: false, // set true to include the metric content size in the design data
218
+ bulkNamesPerNameDesignFiles: false, // set true to generate individual production svgs (-> production pdf in zip download) for each bulk name
218
219
  },
219
220
 
220
221
  vectorizeImageQuality: 45000,