@crowdin/app-project-module 0.105.1 → 0.107.0-cf-0

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.
Files changed (164) hide show
  1. package/out/app-test/integration/get-integration-files.js +0 -1
  2. package/out/app-test/integration/mocks/mock-axios.js +0 -1
  3. package/out/app-test/integration/update-crowdin.js +0 -1
  4. package/out/app-test/integration/update-integration.js +0 -1
  5. package/out/index.d.ts +1 -0
  6. package/out/index.js +30 -12
  7. package/out/middlewares/crowdin-client.d.ts +1 -1
  8. package/out/middlewares/integration-credentials.d.ts +1 -1
  9. package/out/middlewares/integration-credentials.js +4 -1
  10. package/out/middlewares/render-ui-module.d.ts +3 -3
  11. package/out/middlewares/render-ui-module.js +9 -13
  12. package/out/middlewares/ui-module.d.ts +1 -1
  13. package/out/middlewares/ui-module.js +10 -1
  14. package/out/modules/about.d.ts +2 -1
  15. package/out/modules/about.js +10 -3
  16. package/out/modules/ai-prompt-provider/handlers/compile.d.ts +1 -1
  17. package/out/modules/ai-prompt-provider/index.js +2 -2
  18. package/out/modules/ai-provider/handlers/chat-completions.d.ts +1 -1
  19. package/out/modules/ai-provider/handlers/chat-completions.js +0 -1
  20. package/out/modules/ai-provider/handlers/get-model-list.d.ts +1 -1
  21. package/out/modules/ai-provider/index.js +2 -2
  22. package/out/modules/ai-provider/types.d.ts +2 -2
  23. package/out/modules/ai-provider/util/index.js +0 -2
  24. package/out/modules/ai-request-processors/handler.d.ts +1 -1
  25. package/out/modules/ai-tools/handlers/tool-calls.d.ts +1 -1
  26. package/out/modules/ai-tools/index.js +1 -1
  27. package/out/modules/api/api.js +0 -1
  28. package/out/modules/auth-guard/handlers/verify.d.ts +1 -1
  29. package/out/modules/auth-guard/index.js +1 -1
  30. package/out/modules/automation-action/handlers/execute.d.ts +1 -1
  31. package/out/modules/automation-action/handlers/input-schema.d.ts +1 -1
  32. package/out/modules/automation-action/handlers/output-schema.d.ts +1 -1
  33. package/out/modules/automation-action/handlers/validate-settings.d.ts +1 -1
  34. package/out/modules/automation-action/index.js +1 -1
  35. package/out/modules/context-menu/index.js +2 -2
  36. package/out/modules/custom-mt/handlers/translate.d.ts +2 -2
  37. package/out/modules/custom-mt/handlers/translate.js +53 -4
  38. package/out/modules/custom-mt/index.js +6 -3
  39. package/out/modules/custom-mt/types.d.ts +14 -2
  40. package/out/modules/custom-spell-check/handlers/get-languages-list.d.ts +1 -1
  41. package/out/modules/custom-spell-check/handlers/spell-check.d.ts +1 -1
  42. package/out/modules/custom-spell-check/index.js +4 -4
  43. package/out/modules/editor-right-panel/index.js +1 -1
  44. package/out/modules/external-qa-check/handlers/validate.d.ts +1 -1
  45. package/out/modules/external-qa-check/index.js +2 -2
  46. package/out/modules/file-processing/handlers/custom-file-format.d.ts +7 -2
  47. package/out/modules/file-processing/handlers/custom-file-format.js +59 -19
  48. package/out/modules/file-processing/handlers/file-download.d.ts +1 -1
  49. package/out/modules/file-processing/handlers/file-download.js +5 -0
  50. package/out/modules/file-processing/handlers/pre-post-process.d.ts +1 -1
  51. package/out/modules/file-processing/handlers/pre-post-process.js +34 -14
  52. package/out/modules/file-processing/handlers/translations-alignment.d.ts +1 -1
  53. package/out/modules/file-processing/index.js +12 -2
  54. package/out/modules/file-processing/util/defaults.js +50 -6
  55. package/out/modules/file-processing/util/files.js +2 -1
  56. package/out/modules/form-data-display.d.ts +1 -1
  57. package/out/modules/form-data-save.d.ts +1 -1
  58. package/out/modules/install.d.ts +1 -1
  59. package/out/modules/integration/handlers/crowdin-file-progress.d.ts +1 -1
  60. package/out/modules/integration/handlers/crowdin-files.d.ts +1 -1
  61. package/out/modules/integration/handlers/crowdin-project.d.ts +1 -1
  62. package/out/modules/integration/handlers/crowdin-update.d.ts +1 -1
  63. package/out/modules/integration/handlers/crowdin-webhook.d.ts +1 -1
  64. package/out/modules/integration/handlers/integration-data.d.ts +1 -1
  65. package/out/modules/integration/handlers/integration-login.d.ts +1 -1
  66. package/out/modules/integration/handlers/integration-logout.d.ts +1 -1
  67. package/out/modules/integration/handlers/integration-update.d.ts +1 -1
  68. package/out/modules/integration/handlers/integration-webhook.d.ts +1 -1
  69. package/out/modules/integration/handlers/invite-users.d.ts +1 -1
  70. package/out/modules/integration/handlers/job-cancel.d.ts +1 -1
  71. package/out/modules/integration/handlers/job-info-deprecated.d.ts +1 -1
  72. package/out/modules/integration/handlers/job-info.d.ts +1 -1
  73. package/out/modules/integration/handlers/job-list.d.ts +1 -1
  74. package/out/modules/integration/handlers/main.d.ts +1 -1
  75. package/out/modules/integration/handlers/main.js +13 -1
  76. package/out/modules/integration/handlers/oauth-login.d.ts +1 -1
  77. package/out/modules/integration/handlers/oauth-login.js +12 -3
  78. package/out/modules/integration/handlers/oauth-polling.d.ts +2 -2
  79. package/out/modules/integration/handlers/oauth-polling.js +5 -3
  80. package/out/modules/integration/handlers/oauth-url.d.ts +1 -1
  81. package/out/modules/integration/handlers/settings-save.d.ts +1 -1
  82. package/out/modules/integration/handlers/settings.d.ts +1 -1
  83. package/out/modules/integration/handlers/sync-settings-save.d.ts +1 -1
  84. package/out/modules/integration/handlers/sync-settings-save.js +0 -2
  85. package/out/modules/integration/handlers/sync-settings.d.ts +1 -1
  86. package/out/modules/integration/handlers/user-errors.d.ts +1 -1
  87. package/out/modules/integration/handlers/users.d.ts +1 -1
  88. package/out/modules/integration/index.js +13 -34
  89. package/out/modules/integration/util/cron.js +1 -10
  90. package/out/modules/integration/util/defaults.js +31 -28
  91. package/out/modules/integration/util/files.js +18 -11
  92. package/out/modules/integration/util/job.js +0 -4
  93. package/out/modules/integration/util/snapshot.js +1 -5
  94. package/out/modules/integration/util/webhooks.js +1 -8
  95. package/out/modules/manifest.js +15 -13
  96. package/out/modules/modal/index.js +2 -2
  97. package/out/modules/organization-menu/index.js +5 -4
  98. package/out/modules/organization-settings-menu/index.js +5 -4
  99. package/out/modules/profile-resources-menu/index.js +5 -4
  100. package/out/modules/profile-settings-menu/index.js +5 -4
  101. package/out/modules/project-menu/index.js +1 -1
  102. package/out/modules/project-menu-crowdsource/index.js +1 -1
  103. package/out/modules/project-reports/index.js +3 -2
  104. package/out/modules/project-tools/index.js +3 -2
  105. package/out/modules/status.d.ts +1 -1
  106. package/out/modules/status.js +12 -3
  107. package/out/modules/subscription-paid.d.ts +1 -1
  108. package/out/modules/uninstall.d.ts +1 -1
  109. package/out/modules/webhooks/handlers/webhook-handler.d.ts +1 -1
  110. package/out/modules/webhooks/handlers/webhook-handler.js +30 -15
  111. package/out/modules/webhooks/types.d.ts +7 -0
  112. package/out/modules/workflow-step-type/handlers/delete-step.d.ts +1 -1
  113. package/out/modules/workflow-step-type/handlers/step-settings-save.d.ts +1 -1
  114. package/out/modules/workflow-step-type/index.js +2 -2
  115. package/out/storage/d1.d.ts +107 -0
  116. package/out/storage/d1.js +829 -0
  117. package/out/storage/index.js +8 -2
  118. package/out/storage/mysql.js +0 -3
  119. package/out/storage/postgre.js +0 -1
  120. package/out/storage/sqlite.js +0 -3
  121. package/out/types.d.ts +41 -2
  122. package/out/util/axios.js +0 -1
  123. package/out/util/credentials-masker.d.ts +1 -1
  124. package/out/util/cron.d.ts +29 -0
  125. package/out/util/cron.js +87 -0
  126. package/out/util/index.d.ts +12 -1
  127. package/out/util/index.js +54 -6
  128. package/out/util/jsx-renderer.d.ts +6 -0
  129. package/out/util/jsx-renderer.js +13 -0
  130. package/out/util/logger.js +0 -4
  131. package/out/util/static-files.d.ts +19 -0
  132. package/out/util/static-files.js +87 -0
  133. package/out/views/AboutPage.d.ts +10 -0
  134. package/out/views/AboutPage.js +76 -0
  135. package/out/views/ErrorPage.d.ts +18 -0
  136. package/out/views/ErrorPage.js +56 -0
  137. package/out/views/FormPage.d.ts +14 -0
  138. package/out/views/FormPage.js +28 -0
  139. package/out/views/InstallPage.d.ts +10 -0
  140. package/out/views/InstallPage.js +22 -0
  141. package/out/views/LoginPage.d.ts +33 -0
  142. package/out/views/LoginPage.js +200 -0
  143. package/out/views/MainPage.d.ts +81 -0
  144. package/out/views/MainPage.js +1784 -0
  145. package/out/views/OAuthPage.d.ts +7 -0
  146. package/out/views/OAuthPage.js +17 -0
  147. package/out/views/SubscriptionPage.d.ts +7 -0
  148. package/out/views/SubscriptionPage.js +26 -0
  149. package/out/views/index.d.ts +13 -0
  150. package/out/views/index.js +25 -0
  151. package/out/views/layout/Head.d.ts +9 -0
  152. package/out/views/layout/Head.js +54 -0
  153. package/package.json +33 -18
  154. package/out/util/handlebars.d.ts +0 -1
  155. package/out/util/handlebars.js +0 -46
  156. package/out/views/about.handlebars +0 -102
  157. package/out/views/error.handlebars +0 -54
  158. package/out/views/form.handlebars +0 -31
  159. package/out/views/install.handlebars +0 -16
  160. package/out/views/login.handlebars +0 -332
  161. package/out/views/main.handlebars +0 -2042
  162. package/out/views/oauth.handlebars +0 -11
  163. package/out/views/partials/head.handlebars +0 -53
  164. package/out/views/subscription.handlebars +0 -26
@@ -0,0 +1,1784 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MainPage = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const Head_1 = require("./layout/Head");
9
+ const renderConfigField = (field, suffix = 'settings') => {
10
+ var _a;
11
+ if (!field.key) {
12
+ return field.labelHtml ? (react_1.default.createElement("crowdin-p", Object.assign({}, (field.dependencySettings ? { 'data-dependency': field.dependencySettings } : {}), { dangerouslySetInnerHTML: { __html: field.labelHtml } }))) : (react_1.default.createElement("crowdin-p", Object.assign({}, (field.dependencySettings ? { 'data-dependency': field.dependencySettings } : {})), field.label));
13
+ }
14
+ const commonProps = Object.assign(Object.assign(Object.assign({ id: `${field.key}-${suffix}`, key: field.key, label: field.label }, (field.helpText ? { 'help-text': field.helpText } : {})), (field.helpTextHtml ? { 'help-text-html': field.helpTextHtml } : {})), (field.dependencySettings ? { 'data-dependency': field.dependencySettings } : {}));
15
+ switch (field.type) {
16
+ case 'checkbox':
17
+ return (react_1.default.createElement("crowdin-checkbox", Object.assign({}, commonProps, { value: "false", "use-switch": true }, (field.defaultValue === true ? { checked: String(field.defaultValue) } : {}), { onchange: "settingsFieldChange(this)" })));
18
+ case 'select':
19
+ return (react_1.default.createElement("crowdin-select", Object.assign({}, commonProps, (field.isMulti ? { 'is-multi': true, 'close-on-select': 'false' } : {}), (field.isSearchable ? { 'is-searchable': true } : {}), { "is-position-fixed": true, onchange: "settingsFieldChange(this)" }), (_a = field.options) === null || _a === void 0 ? void 0 : _a.map((option) => {
20
+ const isSelected = Array.isArray(field.defaultValue)
21
+ ? field.defaultValue.includes(option.value)
22
+ : field.defaultValue === option.value;
23
+ return (react_1.default.createElement("option", Object.assign({ value: option.value }, (isSelected ? { selected: true } : {})), option.label));
24
+ })));
25
+ case 'text':
26
+ return (react_1.default.createElement("crowdin-input", Object.assign({}, commonProps, { "with-fixed-height": true, value: field.defaultValue || '', onchange: "settingsFieldChange(this)" })));
27
+ case 'textarea':
28
+ return (react_1.default.createElement("crowdin-textarea", Object.assign({}, commonProps, { value: field.defaultValue || '', onchange: "settingsFieldChange(this)" })));
29
+ case 'notice':
30
+ return (react_1.default.createElement("crowdin-alert", Object.assign({}, (field.noticeType ? { type: field.noticeType } : {}), (field.label ? { title: field.label } : {}), (field.noIcon ? { 'no-icon': String(field.noIcon) } : {}), (field.dependencySettings ? { 'data-dependency': field.dependencySettings } : {}), { dangerouslySetInnerHTML: { __html: field.helpText || '' } })));
31
+ default:
32
+ return null;
33
+ }
34
+ };
35
+ const MainPage = (props) => {
36
+ const { name, userId, notice, checkSubscription, isManager, isOwner, infoModal, configurationFields, config = '{}', syncNewElements, withCronSync, webhooks, integrationOneLevelFetching, integrationSearchListener, integrationPagination, uploadTranslations, excludedTargetLanguages, forcePushSources, forcePushTranslations, filtering, hasOrganization, zenModeUrl, asyncProgress = { checkInterval: 3000 }, reloadOnConfigSave, progressiveCrowdinFilesLoading, sentryData, } = props;
37
+ // Build integration button menu items
38
+ const integrationButtonMenuItems = [];
39
+ if (uploadTranslations) {
40
+ integrationButtonMenuItems.push({
41
+ label: 'Sync translations',
42
+ title: 'Sync translations to Crowdin',
43
+ action: 'uploadTranslations',
44
+ });
45
+ }
46
+ if (excludedTargetLanguages) {
47
+ integrationButtonMenuItems.push({
48
+ label: 'Select target languages',
49
+ title: 'Upload file for translation into selected languages',
50
+ action: 'excludedTargetLanguages',
51
+ });
52
+ }
53
+ if (forcePushSources) {
54
+ integrationButtonMenuItems.push({
55
+ label: 'Force Sync',
56
+ title: 'Force sync files to Crowdin',
57
+ action: 'forcePushSources',
58
+ });
59
+ }
60
+ const crowdinButtonMenuItems = [];
61
+ if (forcePushTranslations) {
62
+ crowdinButtonMenuItems.push({
63
+ label: 'Force Sync Translations',
64
+ action: 'forcePushTranslations',
65
+ });
66
+ }
67
+ return (react_1.default.createElement("html", { lang: "en" },
68
+ react_1.default.createElement(Head_1.Head, { sentryData: sentryData }),
69
+ react_1.default.createElement("body", null,
70
+ react_1.default.createElement("div", { className: "i_w" },
71
+ react_1.default.createElement("div", { className: "top" },
72
+ notice && (react_1.default.createElement("crowdin-alert", Object.assign({ id: "notice", title: notice.title }, (!notice.icon ? { 'no-icon': 'true' } : {}), (notice.type ? { type: notice.type } : {}), { style: { display: 'none', marginBottom: '12px' } }),
73
+ react_1.default.createElement("div", { className: "box-center" },
74
+ react_1.default.createElement("p", { className: "m-0", dangerouslySetInnerHTML: { __html: notice.content } })),
75
+ notice.close && (react_1.default.createElement("crowdin-button", { onclick: "closeAlert(this, 'notice')", className: "dismiss-alert", icon: true }, "close")))),
76
+ checkSubscription && (react_1.default.createElement("crowdin-alert", { id: "subscription-info", "no-icon": "true", type: "warning", style: { display: 'none', marginBottom: '12px' } },
77
+ react_1.default.createElement("div", { className: "box-center" },
78
+ react_1.default.createElement("p", { className: "m-0" }),
79
+ react_1.default.createElement("crowdin-button", { class: "ml-1", primary: true, onclick: "window.open(subscriptionLink,'_blank')" }, "Subscribe")))),
80
+ react_1.default.createElement("div", { id: "buttons" },
81
+ react_1.default.createElement("crowdin-button", { id: "show-integration-btn", class: "hidden", "icon-before": "arrow_back", onclick: "showIntegration();" }, "Integration"),
82
+ react_1.default.createElement("crowdin-button", { id: "show-error-logs-btn", "icon-before": "list", onclick: "showErrorLogs();" }, "Error logs"),
83
+ isManager && (react_1.default.createElement("crowdin-button", { "icon-before": "link", onclick: "showPermissionsDialog()" }, "Share")),
84
+ infoModal && (react_1.default.createElement("crowdin-button", { "icon-before": "info", onclick: "openModal(infoModal);" }, infoModal.title)),
85
+ configurationFields && (react_1.default.createElement("crowdin-button", { "icon-before": "settings", onclick: "openModal(settingsModal);fillSettingsForm();" }, "Settings")),
86
+ react_1.default.createElement("crowdin-button", Object.assign({}, (!isOwner ? { disabled: true, title: 'Only the owner can log out' } : {}), { "icon-before": "account_circle", onclick: "integrationLogout()" }), "Log out"))),
87
+ react_1.default.createElement("crowdin-simple-integration", Object.assign({ "async-progress": true }, ((syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.crowdin) ? { 'skip-crowdin-auto-schedule': true } : {}), ((syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration) ? { 'skip-integration-auto-schedule': true } : {}), ((withCronSync === null || withCronSync === void 0 ? void 0 : withCronSync.crowdin) || (webhooks === null || webhooks === void 0 ? void 0 : webhooks.crowdin) ? { 'crowdin-schedule': 'true' } : {}), ((withCronSync === null || withCronSync === void 0 ? void 0 : withCronSync.integration) || (webhooks === null || webhooks === void 0 ? void 0 : webhooks.integration) ? { 'integration-schedule': 'true' } : {}), (integrationOneLevelFetching ? { 'integration-one-level-fetching': 'true' } : {}), (integrationSearchListener ? { 'allow-integration-filter-change-listener': 'true' } : {}), (integrationPagination ? { 'integration-load-more-files': 'true' } : {}), { "integration-name": name, "integration-logo": "logo.png" }, (integrationButtonMenuItems.length > 0
88
+ ? { 'integration-button-menu-items': JSON.stringify(integrationButtonMenuItems) }
89
+ : {}), (crowdinButtonMenuItems.length > 0 ? { 'crowdin-button-menu-items': JSON.stringify(crowdinButtonMenuItems) } : {}), ((filtering === null || filtering === void 0 ? void 0 : filtering.crowdinLanguages) ? { 'crowdin-filter': true } : {}), ((filtering === null || filtering === void 0 ? void 0 : filtering.integrationFilterConfig)
90
+ ? { 'integration-filter': true, 'integration-filter-config': filtering.integrationFilterConfig }
91
+ : {}))),
92
+ react_1.default.createElement("div", { id: "user-errors", className: "hidden" },
93
+ react_1.default.createElement("crowdin-show-as-table", { "is-loading": true, id: "user-errors-table", "is-searchable": true, "total-records": "25", "search-placeholder": "Search something" }),
94
+ react_1.default.createElement("crowdin-alert", null, "This table displays the most recent error logs from the past month. Logs older than one month will be automatically deleted."))),
95
+ react_1.default.createElement("crowdin-toasts", null),
96
+ react_1.default.createElement("crowdin-async-progress", { cancelAsyncAction: "" }),
97
+ react_1.default.createElement("crowdin-modal", { style: { display: 'none' }, id: "subscription-modal", "modal-width": "50", "close-button": "false" },
98
+ react_1.default.createElement("div", null,
99
+ react_1.default.createElement("crowdin-alert", { type: "warning" },
100
+ "Subscribe to continue using the ",
101
+ name,
102
+ " app.")),
103
+ react_1.default.createElement("div", { slot: "footer" },
104
+ react_1.default.createElement("crowdin-button", { primary: true, onclick: "window.open(subscriptionLink,'_blank')" }, "Subscribe"),
105
+ react_1.default.createElement("crowdin-button", { outlined: true, onclick: "window.open('https://crowdin.com/contacts','_blank')" }, "Contact us"),
106
+ react_1.default.createElement("crowdin-button", { class: "ml-10", secondary: true, onclick: "integrationLogout()" }, "Log out"))),
107
+ react_1.default.createElement("crowdin-modal", { style: { display: 'none' }, id: "permissions-modal", "modal-title": "Share", "close-button-title": "Close", "close-button": true, "body-overflow-unset": true },
108
+ react_1.default.createElement("div", { className: "loader hidden" },
109
+ react_1.default.createElement("crowdin-progress-indicator", null)),
110
+ react_1.default.createElement("div", { className: "permissions-modal-content" },
111
+ react_1.default.createElement("div", { className: "select-users-block" },
112
+ react_1.default.createElement("crowdin-users-select", { "allow-new-options": true, "is-multi": true, "is-searchable": true, id: "users", key: "users", label: "Users", "help-text": "Search for members by name, username, email, or invite new ones using their email.", "is-position-fixed": true, onchange: "checkUsers()" })),
113
+ react_1.default.createElement("div", { className: "select-users-info" },
114
+ react_1.default.createElement("div", { className: "confirm-users-block mt-2 hidden" },
115
+ react_1.default.createElement("table", null,
116
+ react_1.default.createElement("thead", null,
117
+ react_1.default.createElement("tr", null,
118
+ react_1.default.createElement("th", { style: { width: '40%' } }, "Access"),
119
+ react_1.default.createElement("th", { style: { width: '35%' } }, "Users"),
120
+ react_1.default.createElement("th", { className: "status", style: { width: '20%' } }, "Upcoming changes"))),
121
+ react_1.default.createElement("tbody", null,
122
+ hasOrganization && (react_1.default.createElement("tr", { className: "organization-invite" },
123
+ react_1.default.createElement("td", null,
124
+ react_1.default.createElement("crowdin-p", null, "Registration in Organization"),
125
+ react_1.default.createElement("div", { className: "permission-description" })),
126
+ react_1.default.createElement("td", { className: "affected-users" }),
127
+ react_1.default.createElement("td", { className: "status" }))),
128
+ react_1.default.createElement("tr", { className: "project-invite" },
129
+ react_1.default.createElement("td", null,
130
+ react_1.default.createElement("crowdin-p", null, "Project Manager Access")),
131
+ react_1.default.createElement("td", { className: "affected-users" }),
132
+ react_1.default.createElement("td", { className: "status" })),
133
+ react_1.default.createElement("tr", { className: "application-settings-invite" },
134
+ react_1.default.createElement("td", null,
135
+ react_1.default.createElement("crowdin-p", null, "Application Visibility Access"),
136
+ react_1.default.createElement("div", { className: "permission-description" })),
137
+ react_1.default.createElement("td", { className: "affected-users" }),
138
+ react_1.default.createElement("td", { className: "status" })),
139
+ react_1.default.createElement("tr", { className: "application-credentials-invite" },
140
+ react_1.default.createElement("td", null,
141
+ react_1.default.createElement("crowdin-p", null, "Access to the Integration"),
142
+ react_1.default.createElement("div", { className: "permission-description" }, "This provides access to sync files and modify the current integration settings.")),
143
+ react_1.default.createElement("td", { className: "affected-users" }),
144
+ react_1.default.createElement("td", { className: "status" }))))),
145
+ zenModeUrl && (react_1.default.createElement("div", { className: "mt-2" },
146
+ react_1.default.createElement("crowdin-input", { "with-fixed-height": true, label: "Zen Mode link", "help-text": "This focused view allows you to concentrate solely on the Integrations section, eliminating other distractions.", value: zenModeUrl, name: "zenModeLink", "with-copy-button": true }))))),
147
+ react_1.default.createElement("div", { slot: "footer" },
148
+ react_1.default.createElement("crowdin-button", { id: "confirm-users-btn", outlined: true, onclick: "inviteUsers()" }, "Save"))),
149
+ excludedTargetLanguages && (react_1.default.createElement("crowdin-modal", { "modal-width": "50", "modal-title": "Select Target Languages for Translation", id: "excluded-languages" })),
150
+ infoModal && (react_1.default.createElement("crowdin-modal", { style: { display: 'none' }, id: "info-modal", "modal-width": "50", "modal-title": infoModal.title, "close-button-title": "Close" },
151
+ react_1.default.createElement("div", { dangerouslySetInnerHTML: { __html: infoModal.content } }))),
152
+ configurationFields && (react_1.default.createElement("crowdin-modal", { style: { display: 'none' }, id: "settings-modal", "modal-width": "65", "modal-title": "Settings", "close-button-title": "Close" },
153
+ react_1.default.createElement("div", { className: "loader hidden" },
154
+ react_1.default.createElement("crowdin-progress-indicator", null)),
155
+ react_1.default.createElement("div", { id: "modal-content" },
156
+ react_1.default.createElement("crowdin-tabs", { "tabs-config": JSON.stringify(configurationFields.map((f) => f.name)), "active-tab": "1" }, configurationFields.map((tab, tabIndex) => (react_1.default.createElement("div", { slot: `tab-${tabIndex + 1}` },
157
+ react_1.default.createElement("div", { className: "crowdin-form" }, tab.fields.map((field) => (react_1.default.createElement(react_1.default.Fragment, null,
158
+ renderConfigField(field),
159
+ react_1.default.createElement("div", Object.assign({ style: { padding: '8px' } }, (field.dependencySettings ? { 'data-dependency': field.dependencySettings } : {})))))))))))),
160
+ react_1.default.createElement("div", { slot: "footer" },
161
+ react_1.default.createElement("crowdin-button", { id: "settings-save-btn", outlined: true, onclick: "saveSettings()" }, "Save")))),
162
+ ((syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.crowdin) || (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration)) && (react_1.default.createElement("crowdin-modal", { style: { display: 'none' }, id: "confirm-schedule-modal", "modal-width": "50", "modal-title": "Synchronization options", "close-button-title": "Close", "close-button": "true", "body-overflow-unset": true },
163
+ react_1.default.createElement("crowdin-checkbox", { id: "selected-files", name: "selected-files", label: "Selected files", class: "hydrated", onchange: "onChangeAutoSynchronizationOptions(this)" }),
164
+ react_1.default.createElement("crowdin-checkbox", { id: "all-folder-files", name: "all-folder-files", label: "All files in the folder", class: "hydrated", hidden: true, onchange: "onChangeAutoSynchronizationOptions(this)" }),
165
+ react_1.default.createElement("crowdin-checkbox", { id: "new-files", name: "new-files", label: "New files", class: "hydrated", onchange: "onChangeAutoSynchronizationOptions(this)" }),
166
+ react_1.default.createElement("div", { slot: "footer" },
167
+ react_1.default.createElement("crowdin-button", { outlined: true, id: "save-schedule-sync", onclick: "saveScheduleSync()" }, "Save")))),
168
+ react_1.default.createElement("crowdin-modal", { style: { display: 'none' }, id: "user-error-detail", "close-button-title": "Close", "close-button": "true" }),
169
+ react_1.default.createElement("script", { type: "text/javascript", dangerouslySetInnerHTML: {
170
+ __html: generateMainScript({
171
+ name,
172
+ userId,
173
+ syncNewElements,
174
+ withCronSync,
175
+ webhooks,
176
+ integrationOneLevelFetching,
177
+ integrationSearchListener,
178
+ integrationPagination,
179
+ config,
180
+ configurationFields,
181
+ excludedTargetLanguages,
182
+ infoModal,
183
+ checkSubscription,
184
+ reloadOnConfigSave,
185
+ asyncProgress,
186
+ notice,
187
+ progressiveCrowdinFilesLoading,
188
+ }),
189
+ } }))));
190
+ };
191
+ exports.MainPage = MainPage;
192
+ // Helper function to generate main JavaScript
193
+ function generateMainScript(options) {
194
+ const { name, userId, syncNewElements, withCronSync, webhooks, integrationOneLevelFetching, integrationSearchListener, integrationPagination, config, configurationFields, excludedTargetLanguages, infoModal, checkSubscription, reloadOnConfigSave, asyncProgress, notice, progressiveCrowdinFilesLoading, } = options;
195
+ return `
196
+ document.body.addEventListener('refreshFilesList', (e) => {
197
+ if (e.detail.refreshIntegration) {
198
+ getIntegrationData({ hardReload: true });
199
+ } else if (e.detail.refreshCrowdin) {
200
+ getCrowdinData();
201
+ }
202
+ });
203
+
204
+ document.body.addEventListener('crowdinFilesFolderToggled', (event) => {
205
+ if (event.detail.componentId === 'crowdin-files' && event.detail.isOpen && event.detail.type === '1') {
206
+ getFileProgress(event.detail.id);
207
+ }
208
+ ${integrationOneLevelFetching
209
+ ? `
210
+ if (event.detail.componentId === 'integration-files' && event.detail.isOpen) {
211
+ getIntegrationData({ parentId: event.detail.id, recursion: event.detail.recursion });
212
+ }
213
+ `
214
+ : ''}
215
+ });
216
+
217
+ document.body.addEventListener('uploadFilesToCrowdin', uploadFilesToCrowdin);
218
+ document.body.addEventListener('uploadFilesToIntegration', uploadFilesToIntegration);
219
+ document.body.addEventListener('cancelAsyncAction', (e) => {
220
+ cancelJob(e.detail);
221
+ });
222
+
223
+ ${integrationSearchListener
224
+ ? `
225
+ document.body.addEventListener("integrationFilterChange", async (event) => {
226
+ const isEmptySearch = !event.detail || event.detail.trim() === '';
227
+
228
+ if (isEmptySearch && savedIntegrationData && hasLoadMoreElements) {
229
+ appComponent.setIntegrationFilesData(savedIntegrationData);
230
+ appComponent.setIntegrationOpenedFolders(currentOpenedFolders);
231
+ return;
232
+ }
233
+
234
+ if (!isEmptySearch && hasLoadMoreElements) {
235
+ savedIntegrationData = await appComponent.getIntegrationFilesData();
236
+ currentOpenedFolders = await appComponent.getIntegrationOpenedFolders();
237
+ }
238
+
239
+ getIntegrationData({ search: event.detail });
240
+ });
241
+ `
242
+ : ''}
243
+
244
+ const appComponent = document.querySelector('crowdin-simple-integration');
245
+ const subscriptionModal = document.getElementById('subscription-modal');
246
+
247
+ const folderType = '0';
248
+ const fileType = '1';
249
+ const branchType = '2';
250
+
251
+ const JOB_TYPE = {
252
+ updateCrowdin: 'updateCrowdin',
253
+ updateIntegration: 'updateIntegration',
254
+ integrationSyncSettingsSave: 'integrationSyncSettingsSave',
255
+ crowdinSyncSettingsSave: 'crowdinSyncSettingsSave',
256
+ };
257
+
258
+ const JOB_STATUS = {
259
+ created: 'created',
260
+ inProgress: 'inProgress',
261
+ failed: 'failed',
262
+ canceled: 'canceled',
263
+ finished: 'finished',
264
+ };
265
+
266
+ const silentJobs = [
267
+ JOB_TYPE.integrationSyncSettingsSave,
268
+ JOB_TYPE.crowdinSyncSettingsSave,
269
+ ];
270
+
271
+ const asyncJobs = {};
272
+
273
+ let project = {};
274
+ let crowdinData = [];
275
+ let fileToSync = [];
276
+ let hasLoadMoreElements = false;
277
+ let savedIntegrationData = null;
278
+ let currentOpenedFolders = null;
279
+
280
+ getCrowdinData();
281
+ getIntegrationData({});
282
+ getActiveJobs();
283
+
284
+ function integrationLogout() {
285
+ checkOrigin()
286
+ .then(queryParams => fetch(\`api/logout\${queryParams}\`, { method: 'POST' }))
287
+ .then(checkResponse)
288
+ .then(reloadLocation)
289
+ .then(localStorage.removeItem('revised_${name}'))
290
+ .catch(e => catchRejection(e, e.error || 'Looks like you are not logged in'));
291
+ }
292
+
293
+ function transformCrowdinItemToTree(e) {
294
+ const item = {
295
+ parent_id: e.parentId ? e.parentId : '0',
296
+ name: e.name,
297
+ id: e.id,
298
+ customContent: e.customContent,
299
+ labels: e.labels,
300
+ disabled: e.disabled,
301
+ tooltip: e.tooltip,
302
+ };
303
+ if (e.type) {
304
+ item.type = e.type;
305
+ item.node_type = fileType;
306
+ item.failed = e.failed;
307
+ item.excludedTargetLanguages = e.excludedTargetLanguages;
308
+ } else {
309
+ item.node_type = e.nodeType || folderType;
310
+ }
311
+ return item;
312
+ }
313
+
314
+ function getCrowdinData() {
315
+ appComponent.setAttribute('is-crowdin-loading', true);
316
+
317
+ ${progressiveCrowdinFilesLoading
318
+ ? `
319
+ checkOrigin()
320
+ .then(restParams => {
321
+ const dirPromise = fetch('api/crowdin/files' + restParams + '&mode=directories')
322
+ .then(checkResponse)
323
+ .then((directories) => {
324
+ const dirTree = directories.map(transformCrowdinItemToTree);
325
+ crowdinData = dirTree;
326
+ appComponent.setCrowdinFilesData(dirTree);
327
+ return dirTree;
328
+ });
329
+
330
+ const filesPromise = fetch('api/crowdin/files' + restParams + '&mode=files')
331
+ .then(checkResponse);
332
+
333
+ const projectPromise = fetch('api/crowdin/project' + restParams)
334
+ .then(checkResponse);
335
+
336
+ return Promise.all([dirPromise, filesPromise, projectPromise]);
337
+ })
338
+ .then(([dirTree, files, projectData]) => {
339
+ const fileTree = files.map(transformCrowdinItemToTree);
340
+ crowdinData = [...dirTree, ...fileTree];
341
+ appComponent.setCrowdinFilesData(crowdinData);
342
+
343
+ project = projectData;
344
+ const languagesSorted = project.targetLanguages.map(language => ({
345
+ sourceLanguageId: projectData.sourceLanguage.editorCode,
346
+ projectEditorLink: projectData.projectEditorLink,
347
+ ...language,
348
+ })).sort((a, b) => a.name.localeCompare(b.name));
349
+
350
+ if (project.inContext && config?.inContext) {
351
+ languagesSorted.push({...project.inContextPseudoLanguage, inContext: true});
352
+ }
353
+
354
+ setLanguagesForTranslation(languagesSorted);
355
+ appComponent.setCrowdinLanguagesData(languagesSorted);
356
+ })
357
+ `
358
+ : `
359
+ checkOrigin()
360
+ .then(restParams => {
361
+ const filesPromise = fetch('api/crowdin/files' + restParams)
362
+ .then(checkResponse);
363
+
364
+ const projectPromise = fetch('api/crowdin/project' + restParams)
365
+ .then(checkResponse);
366
+
367
+ return Promise.all([filesPromise, projectPromise]);
368
+ })
369
+ .then(([files, projectData]) => {
370
+ const tree = files.map(transformCrowdinItemToTree);
371
+ crowdinData = tree;
372
+ appComponent.setCrowdinFilesData(tree);
373
+
374
+ project = projectData;
375
+ const languagesSorted = project.targetLanguages.map(language => ({
376
+ sourceLanguageId: projectData.sourceLanguage.editorCode,
377
+ projectEditorLink: projectData.projectEditorLink,
378
+ ...language,
379
+ })).sort((a, b) => a.name.localeCompare(b.name));
380
+
381
+ if (project.inContext && config?.inContext) {
382
+ languagesSorted.push({...project.inContextPseudoLanguage, inContext: true});
383
+ }
384
+
385
+ setLanguagesForTranslation(languagesSorted);
386
+ appComponent.setCrowdinLanguagesData(languagesSorted);
387
+ })
388
+ `}
389
+ ${withCronSync || webhooks ? `.then(() => getSyncSettings('crowdin'))` : ''}
390
+ .catch(e => catchRejection(e, 'Can\\'t fetch Crowdin data'))
391
+ .finally(() => (appComponent.setAttribute('is-crowdin-loading', false)));
392
+ }
393
+
394
+ function getIntegrationData(
395
+ {
396
+ hardReload = false,
397
+ parentId = '',
398
+ search = '',
399
+ page = 0,
400
+ recursion = false,
401
+ paginationData,
402
+ } = {},
403
+ recursionState = null
404
+ ) {
405
+ if (!paginationData) {
406
+ appComponent.setAttribute('is-integration-loading', true);
407
+ }
408
+
409
+ return checkOrigin()
410
+ .then(restParams => {
411
+ let url = \`api/integration/data\${restParams}&parent_id=\${encodeURIComponent(parentId)}&search=\${encodeURIComponent(search)}&page=\${page}\`;
412
+ if (paginationData) {
413
+ url += \`&paginationData=\${encodeURIComponent(JSON.stringify(paginationData))}\`;
414
+ }
415
+ return fetch(url);
416
+ })
417
+ .then(checkResponse)
418
+ .then(async (res) => {
419
+ const files = res.data;
420
+ const stopPagination = res.stopPagination;
421
+ const tree = files.map(e => {
422
+ const item = {
423
+ parent_id: e.parentId ? e.parentId : '0',
424
+ name: e.name,
425
+ id: e.id,
426
+ customContent: e.customContent,
427
+ labels: e.labels,
428
+ disabled: e.disabled,
429
+ tooltip: e.tooltip,
430
+ pagination: e.pagination,
431
+ };
432
+ if (e.type) {
433
+ item.isNew = e.isNew;
434
+ item.isUpdated = e.isUpdated;
435
+ item.notSynced = e.notSynced;
436
+ item.synced = e.synced;
437
+ item.type = e.type;
438
+ item.node_type = fileType;
439
+ } else {
440
+ item.node_type = e.nodeType || folderType;
441
+ }
442
+ return item;
443
+ });
444
+
445
+ if ((!paginationData && !parentId && !search) || hardReload) {
446
+ hasLoadMoreElements = tree.some(item => item.node_type === 'load-more');
447
+ }
448
+
449
+ const appIntegrationFiles = appComponent.querySelector('#integration-files');
450
+ if (hardReload) {
451
+ appComponent.setIntegrationFilesData(tree);
452
+ } else if (tree.length) {
453
+ appComponent.pushIntegrationFilesData(tree).then(() => {
454
+ if (parentId) {
455
+ appIntegrationFiles.getSelected().then(async selection => {
456
+ const selectedIds = selection?.filter((node) => node).map(({id}) => id.toString());
457
+ const filteredNodes = tree.filter((node) => !selectedIds.includes(node.id.toString()) && selectedIds.includes(node.parent_id.toString()));
458
+
459
+ if (filteredNodes?.length) {
460
+ const filteredNodesId = filteredNodes.map(({id}) => id.toString());
461
+ await appIntegrationFiles.setSelected(filteredNodesId);
462
+ }
463
+ });
464
+ }
465
+ });
466
+ }
467
+ ${integrationPagination
468
+ ? `
469
+ if (stopPagination) {
470
+ appIntegrationFiles.setAttribute('load-more-disabled', true);
471
+ } else {
472
+ appIntegrationFiles.setAttribute('load-more-disabled', false);
473
+ }
474
+ `
475
+ : ''}
476
+ if (search) {
477
+ const openIds = files.filter(e => !e.type).map(e => e.id);
478
+ appComponent.setIntegrationOpenedFolders(openIds);
479
+ }
480
+
481
+ if (recursion) {
482
+ if (!recursionState) {
483
+ recursionState = {
484
+ openedFolders: new Set(),
485
+ calls: 0,
486
+ setLoading(isLoading) {
487
+ appComponent.setAttribute('is-integration-loading', isLoading);
488
+ },
489
+ };
490
+ recursionState.setLoading(true);
491
+ }
492
+
493
+ if (parentId) {
494
+ recursionState.openedFolders.add(parentId);
495
+ }
496
+
497
+ const folderItems = files.filter(isFolder);
498
+
499
+ if (folderItems.length === 0) {
500
+ return Promise.resolve();
501
+ }
502
+
503
+ folderItems.forEach(folder => recursionState.openedFolders.add(folder.id));
504
+
505
+ const currentOpenedFolders = await appComponent.getIntegrationOpenedFolders();
506
+ appComponent.setIntegrationOpenedFolders([...currentOpenedFolders, ...recursionState.openedFolders]);
507
+
508
+ const recursivePromises = folderItems.map(async folder => {
509
+ recursionState.calls++;
510
+
511
+ try {
512
+ return getIntegrationData({ parentId: folder.id, recursion: true }, recursionState);
513
+ } finally {
514
+ recursionState.calls--;
515
+ if (recursionState.calls === 0) {
516
+ recursionState.setLoading(false);
517
+ }
518
+ }
519
+ });
520
+
521
+ return Promise.all(recursivePromises);
522
+ }
523
+ })
524
+ ${withCronSync || webhooks ? `.then(() => getSyncSettings('integration'))` : ''}
525
+ .catch(e => catchRejection(e, 'Can\\'t fetch ${name} templates'))
526
+ .finally(() => {
527
+ if (!recursionState && !recursion) {
528
+ appComponent.setAttribute('is-integration-loading', false);
529
+ } else if (recursionState && recursionState.calls === 0) {
530
+ appComponent.setAttribute('is-integration-loading', false);
531
+ }
532
+ });
533
+ }
534
+
535
+ function getSyncSettings(provider) {
536
+ checkOrigin()
537
+ .then(restParams => fetch(\`/api/sync-settings/\${provider}\` + restParams))
538
+ .then(checkResponse)
539
+ .then((res) => {
540
+ if (provider === 'crowdin') {
541
+ ${(syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.crowdin)
542
+ ? `appComponent.setCrowdinScheduleSync(res, true, true);`
543
+ : `appComponent.setCrowdinScheduleSync(res);`}
544
+ } else {
545
+ ${(syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration)
546
+ ? `appComponent.setIntegrationScheduleSync(res, true, true);`
547
+ : `appComponent.setIntegrationScheduleSync(res);`}
548
+ }
549
+ })
550
+ .catch(e => catchRejection(e, 'Can\\'t fetch file progress'));
551
+ }
552
+
553
+ function getFileProgress(fileId) {
554
+ checkOrigin()
555
+ .then(restParams => fetch(\`api/crowdin/file-progress/\${fileId}\` + restParams))
556
+ .then(checkResponse)
557
+ .then((res) => (appComponent.addCrowdinFileProgress(res)))
558
+ .catch(e => catchRejection(e, 'Can\\'t fetch file progress'));
559
+ }
560
+
561
+ function uploadFilesToCrowdin(event, languages = []) {
562
+ let files = [];
563
+ let uploadTranslations = false;
564
+ let forcePushSources = false;
565
+ if (event.detail?.action === 'uploadTranslations') {
566
+ files = event.detail.files;
567
+ uploadTranslations = true;
568
+ forcePushSources = true;
569
+ } else if (event.detail?.action === 'excludedTargetLanguages') {
570
+ fileToSync = event.detail.files;
571
+ openLanguageModal();
572
+ return;
573
+ } else if (event.detail?.action === 'forcePushSources') {
574
+ files = event.detail.files;
575
+ forcePushSources = true;
576
+ } else {
577
+ files = event.detail;
578
+ }
579
+
580
+ if (event.detail?.length === 0 || !event.detail) {
581
+ showToast('Select templates which will be pushed to Crowdin');
582
+ return;
583
+ }
584
+ appComponent.setAttribute('is-to-crowdin-process', true);
585
+ let req;
586
+ ${integrationOneLevelFetching
587
+ ? `
588
+ req = files.map(f => ({
589
+ name: f.name,
590
+ id: f.id,
591
+ type: f.type,
592
+ parentId: f.parent_id,
593
+ nodeType: f.node_type,
594
+ isUpdated: f.isUpdated,
595
+ }));
596
+ `
597
+ : `
598
+ req = files.filter(f => f.node_type !== folderType).map(f => ({
599
+ name: f.name,
600
+ id: f.id,
601
+ type: f.type,
602
+ parentId: f.parent_id,
603
+ isUpdated: f.isUpdated,
604
+ }));
605
+ `}
606
+ checkOrigin()
607
+ .then(restParams => fetch(\`api/crowdin/update\${restParams}&uploadTranslations=\${uploadTranslations}&languages=\${languages}&forcePushSources=\${forcePushSources}\`, {
608
+ method: 'POST',
609
+ headers: { 'Content-Type': 'application/json' },
610
+ body: JSON.stringify(req)
611
+ }))
612
+ .then(checkResponse)
613
+ .then((res) => {
614
+ checkJob({
615
+ jobId: res?.jobId,
616
+ jobType: JOB_TYPE.updateCrowdin,
617
+ })
618
+ })
619
+ .catch(e => catchRejection(e, 'Can\\'t upload templates to Crowdin'));
620
+ }
621
+
622
+ function getActiveJobs() {
623
+ checkOrigin()
624
+ .then(restParams => fetch(\`api/jobs\${restParams}\`))
625
+ .then(checkResponse)
626
+ .then((jobs) => {
627
+ if (!Array.isArray(jobs)) {
628
+ return;
629
+ }
630
+
631
+ jobs.forEach((job) => {
632
+ switch (job.type) {
633
+ case JOB_TYPE.updateCrowdin:
634
+ appComponent.setAttribute('is-to-crowdin-process', true);
635
+ break;
636
+ case JOB_TYPE.updateIntegration:
637
+ appComponent.setAttribute('is-to-integration-process', true);
638
+ break;
639
+ case JOB_TYPE.crowdinSyncSettingsSave:
640
+ appComponent.setAttribute(\`is-crowdin-sync-settings-in-progress\`, true)
641
+ break;
642
+ case JOB_TYPE.integrationSyncSettingsSave:
643
+ appComponent.setAttribute(\`is-integration-sync-settings-in-progress\`, true)
644
+ break;
645
+ default:
646
+ }
647
+
648
+ checkJob({
649
+ jobId: job.id,
650
+ jobType: job.type,
651
+ })
652
+ })
653
+ })
654
+ .catch(e => catchRejection(e, 'Sync status check failed'))
655
+ }
656
+
657
+ function cancelJob(jobId) {
658
+ if (asyncJobs[jobId]?.isFailed) {
659
+ return;
660
+ }
661
+
662
+ checkOrigin()
663
+ .then(restParams => fetch('api/jobs' + restParams + '&jobId=' + jobId, {
664
+ method: 'DELETE',
665
+ headers: { 'Content-Type': 'application/json' },
666
+ }))
667
+ .then(checkResponse)
668
+ .then(() => showToast('Cancellation…'))
669
+ .catch(e => catchRejection(e, 'Sync cancellation failed'));
670
+ }
671
+
672
+ function checkJob({jobId, jobType, onSuccess, onError, onFinally}) {
673
+ switch (jobType) {
674
+ case JOB_TYPE.updateCrowdin:
675
+ if (!onFinally) {
676
+ onFinally = (() => {
677
+ appComponent.setAttribute('is-to-crowdin-process', false);
678
+ getCrowdinData();
679
+ });
680
+ }
681
+ break;
682
+ case JOB_TYPE.updateIntegration:
683
+ if (!onFinally) {
684
+ onFinally = (() => appComponent.setAttribute('is-to-integration-process', false));
685
+ }
686
+ break;
687
+ case JOB_TYPE.crowdinSyncSettingsSave:
688
+ if (!onFinally) {
689
+ onFinally = (() => appComponent.setAttribute(\`is-crowdin-sync-settings-in-progress\`, false));
690
+ }
691
+ break;
692
+ case JOB_TYPE.integrationSyncSettingsSave:
693
+ if (!onFinally) {
694
+ onFinally = (() => appComponent.setAttribute(\`is-integration-sync-settings-in-progress\`, false));
695
+ }
696
+ break;
697
+ default:
698
+ }
699
+
700
+ checkOrigin()
701
+ .then(restParams => fetch('api/jobs' + restParams + '&jobId=' + jobId))
702
+ .then(checkResponse)
703
+ .then((job) => {
704
+ const isFailed = [JOB_STATUS.failed, JOB_STATUS.canceled].includes(job.status);
705
+ const progress = isFailed || JOB_STATUS.finished === job.status ? 100 : job.progress;
706
+ const info = JOB_STATUS.canceled === job.status ? \`Cancelled\\n\${job.info || ''}\` : job.info;
707
+
708
+ !silentJobs.find((type) => type === jobType) && pushJobs([ {
709
+ id: job.id,
710
+ title: job.title,
711
+ progress,
712
+ info,
713
+ isFailed,
714
+ } ]);
715
+
716
+ if (isFailed) {
717
+ onError && onError();
718
+ } else if ([JOB_STATUS.created, JOB_STATUS.inProgress].includes(job.status) && job.progress < 100) {
719
+ setTimeout(() => {
720
+ checkJob({jobId, jobType, onSuccess, onError, onFinally});
721
+ }, ${asyncProgress.checkInterval});
722
+ return;
723
+ } else {
724
+ onSuccess && onSuccess(job);
725
+ }
726
+
727
+ onFinally && onFinally(job);
728
+ })
729
+ .catch((e) => {
730
+ onFinally && onFinally();
731
+
732
+ !silentJobs.find((type) => type === jobType) && pushJobs([ {
733
+ id: jobId,
734
+ isFailed: true,
735
+ } ]);
736
+
737
+ showToast('Sync status check failed');
738
+ });
739
+ }
740
+
741
+ function pushJobs(jobs) {
742
+ const el = document.querySelector('crowdin-async-progress');
743
+ if (el && el.pushJobs) {
744
+ el.pushJobs(jobs);
745
+ jobs.forEach((job) => (asyncJobs[job.id] = job));
746
+ } else {
747
+ setTimeout(() => {
748
+ pushJobs(jobs);
749
+ }, 300);
750
+ }
751
+ }
752
+
753
+ function uploadFilesToIntegration(event) {
754
+ let files = {};
755
+ let forcePushTranslations = false;
756
+ if (event.detail?.action === 'forcePushTranslations') {
757
+ files = event.detail.files;
758
+ forcePushTranslations = true;
759
+ } else {
760
+ files = event.detail;
761
+ }
762
+
763
+ if (Object.keys(event.detail).length === 0 || !event.detail) {
764
+ showToast('Select files which will be uploaded to ${name}');
765
+ return;
766
+ }
767
+
768
+ appComponent.setAttribute('is-to-integration-process', true);
769
+ const req = {};
770
+ Object.keys(files)
771
+ .filter(id => crowdinData.find(c => c.id === id).node_type === fileType)
772
+ .forEach(id => (req[id] = files[id]));
773
+ checkOrigin()
774
+ .then(restParams => fetch(\`api/integration/update\${restParams}&forcePushTranslations=\${forcePushTranslations}\`, {
775
+ method: 'POST',
776
+ headers: { 'Content-Type': 'application/json' },
777
+ body: JSON.stringify(req)
778
+ }))
779
+ .then(checkResponse)
780
+ .then((res) => {
781
+ checkJob({
782
+ jobId: res?.jobId,
783
+ jobType: JOB_TYPE.updateIntegration,
784
+ })
785
+ })
786
+ .catch(e => catchRejection(e, 'Can\\'t upload files to ${name}'))
787
+ }
788
+
789
+ function openLanguageModal() {
790
+ openModal(languageModal);
791
+ }
792
+
793
+ async function uploadFilesToCrowdinWithExcludedLanguages() {
794
+ const languageSelect = document.querySelector('#language-select');
795
+ const selectedLanguages = await languageSelect.getValue();
796
+ closeModal(languageModal);
797
+ uploadFilesToCrowdin({ detail: fileToSync }, selectedLanguages);
798
+ }
799
+
800
+ async function setLanguagesForTranslation(languages) {
801
+ const languageIds = languages.map(language => language.id);
802
+ if (languageModal) {
803
+ let modalContent = \`
804
+ <div>
805
+ <crowdin-select
806
+ is-multi
807
+ is-searchable
808
+ is-position-fixed
809
+ close-on-select="false"
810
+ name="languages"
811
+ id="language-select"
812
+ label="Languages"
813
+ >\`;
814
+
815
+ for (const language of languages) {
816
+ modalContent += \`<option value="\${language.id}">\${language.name}</option>\`;
817
+ }
818
+
819
+ modalContent += \`
820
+ </crowdin-select>
821
+ </div>
822
+ <div slot="footer">
823
+ <crowdin-button id="upload-exclude-languages" outlined onclick="uploadFilesToCrowdinWithExcludedLanguages()">
824
+ <div class="integration-icon-button">
825
+ <span>Sync to</span>
826
+ <crowdin-logo is-icon/>
827
+ </div>
828
+ </crowdin-button>
829
+ </div>
830
+ \`;
831
+
832
+ languageModal.innerHTML = modalContent;
833
+
834
+ const select = document.querySelector('#language-select')
835
+ await select.setValue(languageIds);
836
+ }
837
+ }
838
+
839
+ function hideConfirmUsersBlock() {
840
+ const confirmUsersBlock = document.querySelector('.confirm-users-block');
841
+ confirmUsersBlock.classList.add('hidden');
842
+
843
+ const permissionsModal = document.getElementById('permissions-modal');
844
+ permissionsModal.setAttribute('body-overflow-unset', true)
845
+ }
846
+
847
+ function getUserFullName(user) {
848
+ if (user.full_name) {
849
+ return user.full_name;
850
+ }
851
+
852
+ const ownerFullName = user.name || ((user.first_name || '') + ' ' + (user.last_name || '')).trim();
853
+
854
+ return !!ownerFullName && user?.login !== ownerFullName
855
+ ? \`\${ownerFullName} (\${user?.login})\`
856
+ : user?.login;
857
+ }
858
+
859
+ function showPermissionsDialog() {
860
+ hideConfirmUsersBlock();
861
+ openModal(permissions)
862
+ setLoader('#permissions-modal');
863
+ const select = document.getElementById('users');
864
+
865
+ select.value = '[]';
866
+
867
+ Promise.all([
868
+ new Promise((resolve) => {
869
+ checkOrigin()
870
+ .then(restParams => fetch('api/users' + restParams))
871
+ .then(checkResponse)
872
+ .then((res) => {
873
+ resolve(JSON.stringify(res.data.managers));
874
+ })
875
+ }),
876
+ new Promise((resolve) => {
877
+ AP.getUsers(res => {
878
+ const users = Object.values(res).filter(user => user.id !== Number('${userId}'));
879
+
880
+ resolve(users.map(user => \`<option value="\${user.id}">\${sanitizeHTML(getUserFullName(user))}</option>\`).join(''));
881
+ })
882
+ })
883
+ ])
884
+ .then(([managers, users]) => {
885
+ select.innerHTML = users;
886
+ select.value = managers;
887
+ })
888
+ .catch(e => catchRejection(e, 'Can\\'t fetch users'))
889
+ .finally(() => unsetLoader('#permissions-modal'));
890
+ }
891
+
892
+ function showConfirmUsersBlock() {
893
+ const confirmUsersBlock = document.querySelector('.confirm-users-block');
894
+ confirmUsersBlock.classList.remove('hidden');
895
+
896
+ const permissionsModal = document.getElementById('permissions-modal');
897
+ permissionsModal.removeAttribute('body-overflow-unset')
898
+ }
899
+
900
+ function checkUsers() {
901
+ setLoader('#permissions-modal');
902
+
903
+ const select = document.getElementById('users');
904
+
905
+ if (select.value === '[]') {
906
+ hideConfirmUsersBlock();
907
+ unsetLoader('#permissions-modal');
908
+ return;
909
+ }
910
+
911
+ const users = JSON.parse(select.value);
912
+ const user_ids = users.filter(id => parseInt(id));
913
+ const emails = users.filter(id => !parseInt(id));
914
+
915
+ const params = { user_ids, emails };
916
+
917
+ Promise.all([
918
+ new Promise((resolve) => {
919
+ checkOrigin()
920
+ .then(restParams => fetch('api/users' + restParams))
921
+ .then(checkResponse)
922
+ .then((usersResponse) => {
923
+ resolve(usersResponse.data.managers);
924
+ });
925
+ }),
926
+ new Promise((resolve) => {
927
+ AP.validateUsers(params, (res) => {
928
+ resolve(res.data);
929
+ });
930
+ }),
931
+ new Promise((resolve) => {
932
+ AP.getUsers(res => {
933
+ resolve(res);
934
+ });
935
+ })
936
+ ])
937
+ .then(([appManagers, checkUsersResponse, allUsers]) => {
938
+ const newUserEmails = checkUsersResponse.newUserEmails.map(email => ({name: email}));
939
+
940
+ const newProjectManagers = [
941
+ ...newUserEmails,
942
+ ...checkUsersResponse.newProjectManagers.map(user => ({
943
+ name: getUserFullName(user),
944
+ })),
945
+ ];
946
+
947
+ const newApplicationAccessUsers = [
948
+ ...newUserEmails,
949
+ ...checkUsersResponse.newApplicationAccessUsers.map(user => ({
950
+ name: getUserFullName(user),
951
+ })),
952
+ ];
953
+
954
+ const newApplicationManagers = [
955
+ ...newUserEmails,
956
+ ...Object.values(allUsers)
957
+ .filter(user => users.some(id => +id === user.id) && !appManagers.some(id => +id === user.id))
958
+ .map(user => ({name: getUserFullName(user)})),
959
+ ];
960
+
961
+ prepareUsersConfirmBlock({
962
+ ...checkUsersResponse,
963
+ newUserEmails,
964
+ newProjectManagers,
965
+ newApplicationAccessUsers,
966
+ newApplicationManagers,
967
+ });
968
+ })
969
+ .catch(e => catchRejection(e, e?.error || 'Can\\'t invite users'))
970
+ .finally(() => unsetLoader('#permissions-modal'));
971
+ }
972
+
973
+ function inviteUsers() {
974
+ setLoader('#permissions-modal');
975
+
976
+ const select = document.getElementById('users');
977
+ const users = JSON.parse(select.value);
978
+ const user_ids = users.filter(id => parseInt(id));
979
+ const emails = users.filter(id => !parseInt(id));
980
+
981
+ const params = { user_ids, emails };
982
+
983
+ AP.inviteUsers(params, (res) => {
984
+ if (!res.success) {
985
+ catchRejection(res, res?.error?.message || 'Can\\'t invite users')
986
+ unsetLoader('#permissions-modal');
987
+ return;
988
+ }
989
+
990
+ checkOrigin()
991
+ .then(restParams => fetch('api/invite-users' + restParams, {
992
+ method: 'POST',
993
+ headers: { 'Content-Type': 'application/json' },
994
+ body: JSON.stringify(res.data)
995
+ }))
996
+ .then(checkResponse)
997
+ .then((res) => {
998
+ if (!res.success) {
999
+ catchRejection(res, res?.message || 'Can\\'t invite users');
1000
+ return;
1001
+ }
1002
+
1003
+ showToast('Users successfully updated');
1004
+ hideConfirmUsersBlock();
1005
+ closeModal(permissions);
1006
+ })
1007
+ .catch((e) => catchRejection(e, e?.error || 'Can\\'t invite users'))
1008
+ .finally(() => unsetLoader('#permissions-modal'));
1009
+ });
1010
+ }
1011
+
1012
+ function prepareUsersConfirmBlock(usersData) {
1013
+ showConfirmUsersBlock();
1014
+
1015
+ const organizationInvite = document.querySelector('.organization-invite');
1016
+ const projectInvite = document.querySelector('.project-invite');
1017
+ const applicationCredentialsInvite = document.querySelector('.application-credentials-invite');
1018
+
1019
+ const grantedElement = '<crowdin-p>&horbar;</crowdin-p>';
1020
+ const willGrantedElement = '<span class="badge badge-will-be-granted">Will Be Granted</span>';
1021
+ const notAvailableElement = '<span class="badge badge-not-available">Action Required</span>';
1022
+
1023
+ // only in enterprise
1024
+ if (organizationInvite) {
1025
+ const organizationWillGrantElement = \`<span class="badge badge-will-be-granted">Will Be Registered</span>\`;
1026
+
1027
+ processUsersWhoWillBeInvitedToOrganization(organizationInvite, usersData, organizationWillGrantElement, grantedElement, notAvailableElement);
1028
+ }
1029
+
1030
+ processUsersWhoWillBeInvited(projectInvite, usersData.newProjectManagers, willGrantedElement, grantedElement);
1031
+ processUsersWhoWillBeInvited(applicationCredentialsInvite, usersData.newApplicationManagers, willGrantedElement, grantedElement);
1032
+
1033
+ processUsersWhoWillBeInvitedApplicationSettings(usersData, willGrantedElement, grantedElement, notAvailableElement);
1034
+ }
1035
+
1036
+ function processUsersWhoWillBeInvitedToOrganization(element, usersData, willGrantedElement, alreadyGrantedMessage, notAvailableElement) {
1037
+ const tooltip = element.querySelector('.status');
1038
+ const description = element.querySelector('.permission-description');
1039
+ const userList = element.querySelector('.affected-users');
1040
+
1041
+ description.classList.remove('text-warning');
1042
+ description.innerText = '';
1043
+
1044
+ const users = usersData.newUserEmails;
1045
+
1046
+ if (users.length) {
1047
+ if (usersData.inviteRestricted) {
1048
+ description.classList.add('text-warning');
1049
+ description.innerText = 'New user invitations are restricted to administrators.';
1050
+
1051
+ tooltip.innerHTML = notAvailableElement;
1052
+ } else {
1053
+ tooltip.innerHTML = willGrantedElement;
1054
+ }
1055
+
1056
+ let affectedUsers = '<ul>';
1057
+ for (const user of users) {
1058
+ affectedUsers += \`<li><crowdin-p>\${sanitizeHTML(user.name)}</crowdin-p></li>\`;
1059
+ }
1060
+ affectedUsers += '</ul>';
1061
+ userList.innerHTML = affectedUsers;
1062
+ } else {
1063
+ tooltip.innerHTML = alreadyGrantedMessage;
1064
+ userList.innerHTML = alreadyGrantedMessage;
1065
+ }
1066
+ }
1067
+
1068
+ function processUsersWhoWillBeInvited(element, users, grantMessage, alreadyGrantedMessage) {
1069
+ const tooltip = element.querySelector('.status');
1070
+ const userList = element.querySelector('.affected-users');
1071
+
1072
+ if (users.length) {
1073
+ tooltip.innerHTML = grantMessage;
1074
+
1075
+ let affectedUsers = '<ul>';
1076
+ for (const user of users) {
1077
+ affectedUsers += \`<li><crowdin-p>\${sanitizeHTML(user.name)}</crowdin-p></li>\`;
1078
+ }
1079
+ affectedUsers += '</ul>';
1080
+ userList.innerHTML = affectedUsers;
1081
+ } else {
1082
+ tooltip.innerHTML = alreadyGrantedMessage;
1083
+ userList.innerHTML = alreadyGrantedMessage;
1084
+ }
1085
+ }
1086
+
1087
+ function processUsersWhoWillBeInvitedApplicationSettings(usersData, willGrantedElement, grantedElement, notAvailableElement) {
1088
+ const applicationSettingsInvite = document.querySelector('.application-settings-invite');
1089
+
1090
+ const tooltip = applicationSettingsInvite.querySelector('.status');
1091
+ const description = applicationSettingsInvite.querySelector('.permission-description');
1092
+ const userList = applicationSettingsInvite.querySelector('.affected-users');
1093
+
1094
+ let descriptionMessage = 'This can be configured in organization settings (Apps section).';
1095
+ description.classList.remove('text-warning');
1096
+ description.innerText = descriptionMessage;
1097
+
1098
+ let affectedUsers = '<ul>';
1099
+
1100
+ if (!usersData.newApplicationAccessUsers.length) {
1101
+ tooltip.innerHTML = grantedElement;
1102
+ userList.innerHTML = grantedElement;
1103
+ } else {
1104
+ if (usersData.isAdmin) {
1105
+ tooltip.innerHTML = willGrantedElement;
1106
+ for (const user of usersData.newApplicationAccessUsers) {
1107
+ affectedUsers += \`<li><crowdin-p>\${sanitizeHTML(user.name)}</crowdin-p></li>\`;
1108
+ }
1109
+ userList.innerHTML = affectedUsers + '</ul>';
1110
+ } else {
1111
+ descriptionMessage += ' Only organization admins have permission to update this setting.';
1112
+
1113
+ description.classList.add('text-warning');
1114
+ description.innerText = descriptionMessage;
1115
+
1116
+ tooltip.innerHTML = notAvailableElement;
1117
+ for (const user of usersData.newApplicationAccessUsers) {
1118
+ affectedUsers += \`<li><crowdin-p>\${sanitizeHTML(user.name)}</crowdin-p></li>\`;
1119
+ }
1120
+ userList.innerHTML = affectedUsers + '</ul>';
1121
+ }
1122
+ }
1123
+ }
1124
+
1125
+ function setLoader(id) {
1126
+ const loader = document.querySelector(\`\${id} .loader\`);
1127
+ loader.classList.remove('hidden');
1128
+ }
1129
+
1130
+ function unsetLoader(id) {
1131
+ const loader = document.querySelector(\`\${id} .loader\`);
1132
+ setTimeout(function() {
1133
+ loader.classList.add('hidden');
1134
+ }, 500)
1135
+ }
1136
+
1137
+ ${configurationFields
1138
+ ? `
1139
+ const settingsModal = document.getElementById('settings-modal');
1140
+ const settingsSaveBtn = document.getElementById('settings-save-btn');
1141
+ let config = JSON.parse('${config}'.replace(/\\n/g, '\\\\n'));
1142
+ const newCrowdinFiles = config?.['new-crowdin-files'];
1143
+ const newIntegrationFiles = config?.['new-integration-files'];
1144
+
1145
+ function triggerEvent(el, type) {
1146
+ const e = document.createEvent('HTMLEvents');
1147
+ e.initEvent(type, false, true);
1148
+ el.dispatchEvent(e);
1149
+ }
1150
+
1151
+ function fillSettingsForm() {
1152
+ Object.entries(config).forEach(([key, value]) => {
1153
+ const el = document.getElementById(\`\${key}-settings\`);
1154
+ if (el && (value || el.tagName.toLowerCase() === 'crowdin-checkbox')) {
1155
+ if (el.tagName.toLowerCase() === 'crowdin-select') {
1156
+ if (el.hasAttribute('is-multi')) {
1157
+ el.value = JSON.stringify(value);
1158
+ } else {
1159
+ el.value = JSON.stringify([value]);
1160
+ }
1161
+ } else if (el.tagName.toLowerCase() === 'crowdin-checkbox') {
1162
+ el.checked = !!value;
1163
+ } else if (el.tagName.toLowerCase() === 'crowdin-textarea') {
1164
+ el.value = value;
1165
+ el.setValue(value);
1166
+ } else {
1167
+ el.value = value;
1168
+ }
1169
+
1170
+ triggerEvent(el, 'change');
1171
+ }
1172
+ });
1173
+ }
1174
+
1175
+ function saveSettings() {
1176
+ setLoader('#settings-modal');
1177
+ const settingsElements = Array.from(document.querySelectorAll('#modal-content crowdin-tabs > div'));
1178
+ const tags = ['crowdin-checkbox', 'crowdin-select', 'crowdin-input', 'crowdin-textarea'];
1179
+ const configReq = {};
1180
+
1181
+ settingsElements.forEach(tab => {
1182
+ const formElements = Array.from(tab.querySelectorAll('.crowdin-form > *'));
1183
+ formElements
1184
+ .filter(e => tags.includes(e.tagName.toLowerCase()))
1185
+ .forEach(e => {
1186
+ const key = e.getAttribute('key');
1187
+ let value;
1188
+ if (e.tagName.toLowerCase() === 'crowdin-select') {
1189
+ value = JSON.parse(e.value);
1190
+ if (!e.hasAttribute('is-multi')) {
1191
+ value = value.length > 0 ? value[0] : undefined;
1192
+ }
1193
+ } else if (e.tagName.toLowerCase() === 'crowdin-checkbox') {
1194
+ value = !!e.checked;
1195
+ } else {
1196
+ value = e.value;
1197
+ }
1198
+ configReq[key] = value;
1199
+ });
1200
+ });
1201
+
1202
+ let isValidationError = false;
1203
+ settingsSaveBtn.setAttribute('disabled', true);
1204
+ checkOrigin()
1205
+ .then(restParams => fetch('api/settings' + restParams, {
1206
+ method: 'POST',
1207
+ headers: { 'Content-Type': 'application/json' },
1208
+ body: JSON.stringify({ config: configReq })
1209
+ }))
1210
+ .then(checkResponse)
1211
+ .then(() => {
1212
+ showToast('Settings successfully saved');
1213
+ config = configReq;
1214
+ })
1215
+ .catch(e => {
1216
+ if (e?.error && e?.error?.type === 'validation_error') {
1217
+ Object.keys(e?.error?.details).forEach(key => {
1218
+ const el = document.getElementById(\`\${key}-settings\`);
1219
+ if (el) {
1220
+ el.setAttribute('error', 'true');
1221
+ el.setAttribute('error-text', e?.error?.details[key]);
1222
+ }
1223
+ });
1224
+
1225
+ isValidationError = true;
1226
+ showToast(e?.error?.details?.message || 'Can\\'t save settings');
1227
+ settingsSaveBtn.removeAttribute('disabled');
1228
+ return;
1229
+ }
1230
+ catchRejection(e, 'Can\\'t save settings')
1231
+ })
1232
+ .finally(() => {
1233
+ unsetLoader('#settings-modal');
1234
+ settingsSaveBtn.removeAttribute('disabled');
1235
+ if (!isValidationError) {
1236
+ closeModal(settingsModal);
1237
+ ${reloadOnConfigSave
1238
+ ? `
1239
+ getIntegrationData({ hardReload: true });
1240
+ getCrowdinData();
1241
+ `
1242
+ : ''}
1243
+ }
1244
+ });
1245
+ }
1246
+ `
1247
+ : `
1248
+ const settingsModal = undefined;
1249
+ `}
1250
+
1251
+ const permissions = document.getElementById('permissions-modal');
1252
+
1253
+ ${excludedTargetLanguages
1254
+ ? `
1255
+ const languageModal = document.getElementById('excluded-languages');
1256
+ `
1257
+ : `
1258
+ const languageModal = undefined;
1259
+ `}
1260
+
1261
+ ${infoModal
1262
+ ? `
1263
+ const infoModal = document.getElementById('info-modal');
1264
+ `
1265
+ : `
1266
+ const infoModal = undefined;
1267
+ `}
1268
+
1269
+ document.addEventListener('keydown', (event) => {
1270
+ if (event.keyCode == 27) {
1271
+ if (users) {
1272
+ closeModal(permissions);
1273
+ }
1274
+ if (infoModal) {
1275
+ closeModal(infoModal);
1276
+ }
1277
+ if (settingsModal) {
1278
+ closeModal(settingsModal);
1279
+ }
1280
+ if (languageModal) {
1281
+ closeModal(languageModal);
1282
+ }
1283
+ }
1284
+ });
1285
+
1286
+ ${integrationPagination
1287
+ ? `
1288
+ document.body.addEventListener('fileListEnd', (e) => {
1289
+ getIntegrationData({ page: e.detail.page });
1290
+ })
1291
+ `
1292
+ : ''}
1293
+
1294
+ document.body.addEventListener('loadMoreClick', async (e) => {
1295
+ const loadMoreFile = e.detail;
1296
+ const fileComponent = document.getElementById('integration-files')
1297
+
1298
+ await fileComponent.setLoadMoreLoading(loadMoreFile.id, true);
1299
+
1300
+ getIntegrationData({ parentId: e.detail.parent_id, paginationData: e.detail.pagination })
1301
+ .finally(async () => {
1302
+ await fileComponent.setLoadMoreLoading(loadMoreFile.id, false);
1303
+ });
1304
+ });
1305
+
1306
+ ${withCronSync || webhooks
1307
+ ? `
1308
+ const scheduleModal = document.getElementById('confirm-schedule-modal');
1309
+ let syncData;
1310
+
1311
+ document.body.addEventListener('integrationScheduleSync', setIntegrationScheduleSync);
1312
+ document.body.addEventListener('crowdinScheduleSync', setCrowdinScheduleSync);
1313
+ document.body.addEventListener('crowdinDisableSync', disableCrowdinSync);
1314
+ document.body.addEventListener('integrationDisableSync', disableIntegrationSync);
1315
+
1316
+ function getIntegrationFoldersToExpand(allData, syncedFiles) {
1317
+ return Array.isArray(allData)
1318
+ ? allData.filter(
1319
+ node => (node.node_type === folderType && !syncedFiles.find((syncedFile) => syncedFile.parent_id === node.id))
1320
+ ).map(node => ({
1321
+ ...node,
1322
+ nodeType: node.node_type,
1323
+ parentId: node.parent_id,
1324
+ }))
1325
+ : [];
1326
+ }
1327
+
1328
+ async function saveScheduleSync() {
1329
+ const newFile = scheduleModal.querySelector('#new-files').checked || false;
1330
+ const selectedFiles = scheduleModal.querySelector('#selected-files').checked || false;
1331
+ const allFolderFiles = scheduleModal.querySelector('#all-folder-files').checked || false;
1332
+
1333
+ const type = scheduleModal.getAttribute('data-type');
1334
+
1335
+ if (type === 'crowdin') {
1336
+ appComponent.setCrowdinScheduleSync(syncData, newFile, selectedFiles);
1337
+ appComponent.setCrowdinFilesHasSyncOption(true);
1338
+ const syncedFiles = await appComponent.getCrowdinScheduleSync(true);
1339
+ appComponent.setAttribute('is-crowdin-sync-settings-in-progress', true);
1340
+ updateSyncSettings(syncedFiles, 'schedule', 'crowdin');
1341
+ } else if (type === 'integration') {
1342
+ ${(syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration)
1343
+ ? `
1344
+ appComponent.setIntegrationScheduleSync(syncData, newFile, selectedFiles);
1345
+ `
1346
+ : `
1347
+ appComponent.setIntegrationScheduleSync(syncData);
1348
+ `}
1349
+ appComponent.setIntegrationFilesHasSyncOption(true);
1350
+ const syncedFiles = await appComponent.getIntegrationScheduleSync(true);
1351
+ appComponent.setAttribute('is-integration-sync-settings-in-progress', true);
1352
+ ${integrationOneLevelFetching
1353
+ ? `
1354
+ updateSyncSettings(
1355
+ syncedFiles,
1356
+ 'schedule',
1357
+ 'integration',
1358
+ selectedFiles ? getIntegrationFoldersToExpand(syncData, syncedFiles) : [],
1359
+ );
1360
+ `
1361
+ : `
1362
+ if (allFolderFiles) {
1363
+ const integrationExpandedFolders = syncData.filter(file => file.node_type === folderType).map(file => ({
1364
+ ...file,
1365
+ nodeType: file.node_type,
1366
+ parentId: file.parent_id,
1367
+ }));
1368
+ updateSyncSettings(syncedFiles, 'schedule', 'integration', integrationExpandedFolders);
1369
+ } else {
1370
+ updateSyncSettings(syncedFiles, 'schedule', 'integration');
1371
+ }
1372
+ `}
1373
+ }
1374
+
1375
+ closeModal(scheduleModal);
1376
+ }
1377
+
1378
+ async function openScheduleModal(type, options = {}) {
1379
+ const { showAllFolderFiles = false, showNewFiles = true } = options;
1380
+
1381
+ const newFile = scheduleModal.querySelector('#new-files')
1382
+ const selectedFiles = scheduleModal.querySelector('#selected-files');
1383
+ const allFolderFiles = scheduleModal.querySelector('#all-folder-files');
1384
+ let newFileStatus = 'unChecked';
1385
+
1386
+ const integrationSyncFolders = (await document.querySelector('crowdin-simple-integration').getIntegrationScheduleSync(true))
1387
+ .filter(elements => elements.node_type === '0');
1388
+
1389
+ const selectedIntegrationFolders = (await document.getElementById('integration-files').getSelected(true))
1390
+ .filter(elements => elements.node_type === '0');
1391
+
1392
+ for (let i = 0; i < selectedIntegrationFolders.length; i++) {
1393
+ const selectedElement = selectedIntegrationFolders[i];
1394
+
1395
+ const found = integrationSyncFolders.some(integrationElement =>
1396
+ integrationElement.id === selectedElement.id
1397
+ );
1398
+
1399
+ if (found) {
1400
+ newFileStatus = 'checked';
1401
+ } else if (!found && newFileStatus === 'checked') {
1402
+ newFileStatus = 'clear-selection';
1403
+ break;
1404
+ } else {
1405
+ newFileStatus = 'unChecked';
1406
+ }
1407
+ }
1408
+
1409
+ if (showNewFiles) {
1410
+ newFile.removeAttribute('hidden');
1411
+ if (newFileStatus === 'checked') {
1412
+ newFile[newFileStatus] = true;
1413
+ newFile.value = true;
1414
+ newFile.removeAttribute('clear-selection');
1415
+ } else if (newFileStatus === 'clear-selection') {
1416
+ newFile.checked = false;
1417
+ newFile.value = false;
1418
+ newFile.setAttribute(newFileStatus, '');
1419
+ } else {
1420
+ newFile.checked = false;
1421
+ newFile.value = false;
1422
+ newFile.removeAttribute('clear-selection');
1423
+ }
1424
+ } else {
1425
+ newFile.setAttribute('hidden', '');
1426
+ newFile.checked = false;
1427
+ }
1428
+
1429
+ if (showAllFolderFiles) {
1430
+ allFolderFiles.removeAttribute('hidden');
1431
+ allFolderFiles.checked = false;
1432
+ allFolderFiles.value = false;
1433
+ } else {
1434
+ allFolderFiles.setAttribute('hidden', '');
1435
+ allFolderFiles.checked = false;
1436
+ }
1437
+
1438
+ selectedFiles.checked = true;
1439
+ selectedFiles.value = true;
1440
+ scheduleModal.querySelector('#save-schedule-sync').setAttribute('disabled', false);
1441
+ scheduleModal.setAttribute('data-type', type);
1442
+ openModal(scheduleModal);
1443
+ }
1444
+
1445
+ function onChangeAutoSynchronizationOptions() {
1446
+ const newFiles = document.getElementById('new-files');
1447
+ newFiles.removeAttribute('clear-selection');
1448
+
1449
+ const newFilesStatus = newFiles.checked || false;
1450
+ const selectedFiles = document.getElementById('selected-files').checked || false;
1451
+ const allFolderFiles = document.getElementById('all-folder-files').checked || false;
1452
+ const buttonSaveScheduleSync = document.getElementById('save-schedule-sync');
1453
+
1454
+ if (newFilesStatus || selectedFiles || allFolderFiles) {
1455
+ buttonSaveScheduleSync.removeAttribute('disabled');
1456
+ } else {
1457
+ buttonSaveScheduleSync.setAttribute('disabled', true);
1458
+ }
1459
+ }
1460
+
1461
+ async function hasFolder(selectedFiles) {
1462
+ let isFolder;
1463
+ if (Array.isArray(selectedFiles)) {
1464
+ isFolder = selectedFiles.find((file) => file.node_type === folderType || file.node_type === branchType);
1465
+ } else {
1466
+ const files = await appComponent.getCrowdinFilesData();
1467
+ const folders = files.filter((file) => file.node_type === folderType || file.node_type === branchType);
1468
+ isFolder = folders.find((folder) => selectedFiles.hasOwnProperty(folder.id));
1469
+ }
1470
+
1471
+ return isFolder !== undefined;
1472
+ }
1473
+
1474
+ async function setIntegrationScheduleSync(e) {
1475
+ if (e.detail.length === 0) {
1476
+ showToast('Select templates which will be pushed to Crowdin');
1477
+ return;
1478
+ }
1479
+
1480
+ ${(syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration)
1481
+ ? `
1482
+ syncData = e.detail;
1483
+ const isFolder = await hasFolder(e.detail);
1484
+
1485
+ const shouldShowModal = isFolder && !newIntegrationFiles;
1486
+ const showNewFiles = true; // завжди показуємо New files коли є syncNewElements.integration
1487
+ const showAllFolderFiles = hasLoadMoreElements; // показуємо All files якщо є hasLoadMoreElements
1488
+
1489
+ if (shouldShowModal) {
1490
+ await openScheduleModal('integration', { showNewFiles, showAllFolderFiles });
1491
+ return;
1492
+ }
1493
+
1494
+ appComponent.setIntegrationScheduleSync(e.detail);
1495
+ appComponent.setIntegrationFilesHasSyncOption(true);
1496
+ const syncedFiles = await appComponent.getIntegrationScheduleSync(true);
1497
+ `
1498
+ : `
1499
+ syncData = e.detail;
1500
+ const isFolder = await hasFolder(e.detail);
1501
+
1502
+ if (hasLoadMoreElements && isFolder) {
1503
+ await openScheduleModal('integration', { showNewFiles: false, showAllFolderFiles: true });
1504
+ return;
1505
+ }
1506
+
1507
+ const syncedFiles = await appComponent.getIntegrationScheduleSync();
1508
+ `}
1509
+
1510
+ appComponent.setAttribute('is-integration-sync-settings-in-progress', true);
1511
+
1512
+ ${integrationOneLevelFetching
1513
+ ? `
1514
+ updateSyncSettings(syncedFiles, 'schedule', 'integration', getIntegrationFoldersToExpand(e.detail, syncedFiles));
1515
+ `
1516
+ : `
1517
+ if (hasLoadMoreElements) {
1518
+ const integrationExpandedFolders = e.detail.filter(file => file.node_type === folderType).map(file => ({
1519
+ ...file,
1520
+ nodeType: file.node_type,
1521
+ parentId: file.parent_id,
1522
+ }));
1523
+
1524
+ updateSyncSettings(syncedFiles, 'schedule', 'integration', integrationExpandedFolders);
1525
+ } else {
1526
+ updateSyncSettings(syncedFiles, 'schedule', 'integration');
1527
+ }
1528
+ `}
1529
+ }
1530
+
1531
+ async function setCrowdinScheduleSync(e) {
1532
+ if (e.detail.length === 0) {
1533
+ showToast('Select templates which will be pushed to Crowdin');
1534
+ return;
1535
+ }
1536
+
1537
+ ${(syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.crowdin)
1538
+ ? `
1539
+ syncData = e.detail;
1540
+ const isFolder = await hasFolder(e.detail);
1541
+ if (isFolder && !newCrowdinFiles) {
1542
+ await openScheduleModal('crowdin');
1543
+ return;
1544
+ }
1545
+
1546
+ appComponent.setCrowdinScheduleSync(e.detail);
1547
+ appComponent.setCrowdinFilesHasSyncOption(true);
1548
+ const syncedFiles = await appComponent.getCrowdinScheduleSync(true);
1549
+ `
1550
+ : `
1551
+ const syncedFiles = await appComponent.getCrowdinScheduleSync();
1552
+ `}
1553
+ appComponent.setAttribute('is-crowdin-sync-settings-in-progress', true);
1554
+ updateSyncSettings(syncedFiles, 'schedule', 'crowdin');
1555
+ }
1556
+
1557
+ async function disableIntegrationSync(e) {
1558
+ if (e.detail.length === 0) {
1559
+ showToast('Select templates which will be pushed to Crowdin');
1560
+ return;
1561
+ }
1562
+ appComponent.setAttribute('is-integration-sync-settings-in-progress', true);
1563
+ ${(syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration)
1564
+ ? `
1565
+ const syncedFiles = await appComponent.getIntegrationScheduleSync(true);
1566
+ `
1567
+ : `
1568
+ const syncedFiles = await appComponent.getIntegrationScheduleSync();
1569
+ `}
1570
+
1571
+ updateSyncSettings(syncedFiles, 'schedule', 'integration');
1572
+ }
1573
+
1574
+ async function disableCrowdinSync(e) {
1575
+ if (e.detail.length === 0) {
1576
+ showToast('Select templates which will be pushed to Crowdin');
1577
+ return;
1578
+ }
1579
+ appComponent.setAttribute('is-crowdin-sync-settings-in-progress', true);
1580
+ ${(syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.crowdin)
1581
+ ? `
1582
+ const syncedFiles = await appComponent.getCrowdinScheduleSync(true);
1583
+ `
1584
+ : `
1585
+ const syncedFiles = await appComponent.getCrowdinScheduleSync();
1586
+ `}
1587
+
1588
+ updateSyncSettings(syncedFiles, 'schedule', 'crowdin');
1589
+ }
1590
+
1591
+ function updateSyncSettings(files, type, provider, expandIntegrationFolders = []) {
1592
+ checkOrigin()
1593
+ .then(restParams => fetch('api/sync-settings' + restParams, {
1594
+ method: 'POST',
1595
+ headers: { 'Content-Type': 'application/json' },
1596
+ body: JSON.stringify({ files, type, provider, expandIntegrationFolders, hasLoadMoreElements }),
1597
+ }))
1598
+ .then(checkResponse)
1599
+ .then((res) => {
1600
+ checkJob({
1601
+ jobId: res?.jobId,
1602
+ jobType: JOB_TYPE[\`\${provider}SyncSettingsSave\`],
1603
+ })
1604
+ })
1605
+ .catch(e => catchRejection(e, 'Can\\'t save schedule sync settings'));
1606
+ }
1607
+ `
1608
+ : ''}
1609
+
1610
+ ${checkSubscription
1611
+ ? `
1612
+ const subscriptionInfo = document.getElementById('subscription-info');
1613
+ const subscriptionInfoText = subscriptionInfo.getElementsByTagName('p')[0];
1614
+ function getSubscriptionInfo() {
1615
+ checkOrigin()
1616
+ .then(restParams => fetch('api/subscription-info' + restParams))
1617
+ .then(checkResponse)
1618
+ .then((res) => {
1619
+ if (res.showInfo) {
1620
+ subscriptionLink = res.subscribeLink;
1621
+ subscriptionInfoText.textContent = \`Your trial expires in \${res.daysLeft} days.\`
1622
+ subscriptionInfo.style.display = 'block';
1623
+ }
1624
+ });
1625
+ }
1626
+
1627
+ getSubscriptionInfo();
1628
+ `
1629
+ : ''}
1630
+
1631
+ function checkAlert(alert, suffix) {
1632
+ const name = suffix ?? '${name}';
1633
+ const revised = localStorage.getItem(\`revised_\${name}\`) === '1';
1634
+ if (!revised) {
1635
+ alert.style.display = 'block';
1636
+ }
1637
+ }
1638
+ function closeAlert(el, suffix) {
1639
+ const name = suffix ?? '${name}';
1640
+ const alert = el.closest('crowdin-alert');
1641
+ alert.style.display = 'none';
1642
+ localStorage.setItem(\`revised_\${name}\`, 1);
1643
+ }
1644
+
1645
+ ${notice
1646
+ ? `
1647
+ const notice = document.getElementById('notice');
1648
+ checkAlert(notice, 'notice');
1649
+ `
1650
+ : ''}
1651
+
1652
+ function showErrorLogs() {
1653
+ document.getElementById('show-error-logs-btn').classList.add('hidden');
1654
+ document.getElementById('show-integration-btn').classList.remove('hidden');
1655
+
1656
+ appComponent.classList.add('hidden');
1657
+ document.getElementById('user-errors').classList.remove('hidden');
1658
+
1659
+ checkOrigin()
1660
+ .then(restParams => fetch('api/user-errors' + restParams))
1661
+ .then(checkResponse)
1662
+ .then((res) => {
1663
+ const table = document.getElementById('user-errors-table');
1664
+
1665
+ const clickRow = (field, index, item) => {
1666
+ const modal = document.getElementById('user-error-detail');
1667
+ openModal(modal);
1668
+ modal.innerHTML = getUserErrorDetail(item);
1669
+ };
1670
+
1671
+ const formatDate = (field, index, item) => {
1672
+ return item.formattedDate;
1673
+ }
1674
+
1675
+ table.setTableConfig && table.setTableConfig({
1676
+ defaultSortingColumn: "createdAt",
1677
+ defaultSortingDir: "desc",
1678
+ headerFields: [
1679
+ {
1680
+ field: "createdAt",
1681
+ name: "Date",
1682
+ isSortable: true,
1683
+ clickFn: clickRow,
1684
+ formatFn: formatDate,
1685
+ },
1686
+ {
1687
+ field: "action",
1688
+ name: "Action",
1689
+ clickFn: clickRow,
1690
+ },
1691
+ {
1692
+ field: "message",
1693
+ name: "Message",
1694
+ clickFn: clickRow,
1695
+ }
1696
+ ],
1697
+ });
1698
+
1699
+ table.setTableData(JSON.stringify(res));
1700
+ table.setAttribute(\`is-loading\`, false);
1701
+ })
1702
+ .catch(e => catchRejection(e, 'Can\\'t fetch error logs'));
1703
+ }
1704
+
1705
+ function showIntegration() {
1706
+ document.getElementById('show-error-logs-btn').classList.remove('hidden');
1707
+ document.getElementById('show-integration-btn').classList.add('hidden');
1708
+
1709
+ appComponent.classList.remove('hidden');
1710
+ document.getElementById('user-errors').classList.add('hidden');
1711
+ }
1712
+
1713
+ function isJSON(string) {
1714
+ try {
1715
+ JSON.parse(string);
1716
+ return true;
1717
+ } catch (e) {
1718
+ return false;
1719
+ }
1720
+ }
1721
+
1722
+ function getUserErrorDetail(error) {
1723
+ const data = JSON.parse(error.data);
1724
+
1725
+ let html = '<div class="error-detail-table"><table>';
1726
+ html += \`<tr><td>Action</td><td>\${sanitizeHTML(error.action)}</td></tr>\`;
1727
+ if (isJSON(error.message)) {
1728
+ html += \`<tr><td>Message</td><td><pre>\${sanitizeHTML(error.message)}<pre></td></tr>\`;
1729
+ } else {
1730
+ html += \`<tr><td>Message</td><td>\${sanitizeHTML(error.message)}</td></tr>\`;
1731
+ }
1732
+ html += \`<tr><td>Date/time</td><td>\${sanitizeHTML(error.formattedDate)}</td></tr>\`;
1733
+
1734
+ if (data.requestParams) {
1735
+ html += \`<tr><td>Method</td><td>\${sanitizeHTML(data.requestParams.method)}</td></tr>\`;
1736
+ }
1737
+
1738
+ if (data.responseData) {
1739
+ html += \`<tr><td>Response Data</td><td><pre>\${sanitizeHTML(JSON.stringify(data.responseData, null, 2))}</pre></td></tr>\`;
1740
+ }
1741
+
1742
+ if (data.appData) {
1743
+ if (Array.isArray(data.appData)) {
1744
+ html += \`<tr><td>App Data</td><td><pre>\${sanitizeHTML(JSON.stringify(data.appData, null, 2))}</pre></td></tr>\`;
1745
+ } else if (typeof data.appData === 'object') {
1746
+ for (const key in data.appData) {
1747
+ html += \`<tr><td>\${sanitizeHTML(key.charAt(0).toUpperCase() + key.slice(1))}</td><td><pre>\${sanitizeHTML(JSON.stringify(data.appData[key], null, 2))}</pre></td></tr>\`;
1748
+ }
1749
+ } else {
1750
+ html += \`<tr><td>App Data</td><td>\${sanitizeHTML(data.appData)}</td></tr>\`;
1751
+ }
1752
+ }
1753
+
1754
+ html += '</table></div>';
1755
+
1756
+ return html;
1757
+ }
1758
+
1759
+ function sanitizeHTML(str) {
1760
+ const tempDiv = document.createElement('div');
1761
+ tempDiv.textContent = str;
1762
+ return tempDiv.innerHTML;
1763
+ }
1764
+
1765
+ function openModal(modal) {
1766
+ modal.style.display = 'block';
1767
+ modal.open()
1768
+ }
1769
+
1770
+ function closeModal(modal) {
1771
+ modal.style.display = 'none';
1772
+ modal.close()
1773
+ }
1774
+
1775
+ function settingsFieldChange(el) {
1776
+ el.removeAttribute('error');
1777
+ el.removeAttribute('error-text');
1778
+ }
1779
+
1780
+ function isFolder(e) {
1781
+ return !e.type && (e.nodeType === undefined || e.nodeType === folderType || e.nodeType === branchType);
1782
+ }
1783
+ `;
1784
+ }