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