@crowdin/app-project-module 0.87.0 → 0.88.1

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.
@@ -191,7 +191,7 @@ function addDefaultApiEndpoints(app, config) {
191
191
  config,
192
192
  optional: false,
193
193
  checkSubscriptionExpiration: true,
194
- moduleKey: config.projectIntegration.key,
194
+ moduleKey: 'crowdin-files-api',
195
195
  }), (0, integration_credentials_1.default)(config, config.projectIntegration), (0, crowdin_files_1.default)(config, config.projectIntegration));
196
196
  /**
197
197
  * @openapi
@@ -225,7 +225,7 @@ function addDefaultApiEndpoints(app, config) {
225
225
  config,
226
226
  optional: false,
227
227
  checkSubscriptionExpiration: true,
228
- moduleKey: config.projectIntegration.key,
228
+ moduleKey: 'file-translation-progress-api',
229
229
  }), (0, crowdin_file_progress_1.default)(config.projectIntegration));
230
230
  /**
231
231
  * @openapi
@@ -252,7 +252,7 @@ function addDefaultApiEndpoints(app, config) {
252
252
  config,
253
253
  optional: false,
254
254
  checkSubscriptionExpiration: true,
255
- moduleKey: config.projectIntegration.key,
255
+ moduleKey: 'integration-files-api',
256
256
  }), (0, integration_credentials_1.default)(config, config.projectIntegration), (0, integration_data_1.default)(config.projectIntegration));
257
257
  /**
258
258
  * @openapi
@@ -280,7 +280,7 @@ function addDefaultApiEndpoints(app, config) {
280
280
  config,
281
281
  optional: false,
282
282
  checkSubscriptionExpiration: true,
283
- moduleKey: config.projectIntegration.key,
283
+ moduleKey: 'crowdin-update-api',
284
284
  }), (0, integration_credentials_1.default)(config, config.projectIntegration), (0, crowdin_update_1.default)(config, config.projectIntegration));
285
285
  /**
286
286
  * @openapi
@@ -308,7 +308,7 @@ function addDefaultApiEndpoints(app, config) {
308
308
  config,
309
309
  optional: false,
310
310
  checkSubscriptionExpiration: true,
311
- moduleKey: config.projectIntegration.key,
311
+ moduleKey: 'integration-update-api',
312
312
  }), (0, integration_credentials_1.default)(config, config.projectIntegration), (0, integration_update_1.default)(config, config.projectIntegration));
313
313
  /**
314
314
  * @openapi
@@ -338,7 +338,7 @@ function addDefaultApiEndpoints(app, config) {
338
338
  config,
339
339
  optional: false,
340
340
  checkSubscriptionExpiration: true,
341
- moduleKey: config.projectIntegration.key,
341
+ moduleKey: 'job-get-api',
342
342
  }), (0, job_info_1.default)(config));
343
343
  /**
344
344
  * @openapi
@@ -364,7 +364,7 @@ function addDefaultApiEndpoints(app, config) {
364
364
  config,
365
365
  optional: false,
366
366
  checkSubscriptionExpiration: true,
367
- moduleKey: config.projectIntegration.key,
367
+ moduleKey: 'job-cancel-api',
368
368
  }), (0, job_cancel_1.default)(config));
369
369
  /**
370
370
  * @openapi
@@ -390,7 +390,7 @@ function addDefaultApiEndpoints(app, config) {
390
390
  config,
391
391
  optional: false,
392
392
  checkSubscriptionExpiration: true,
393
- moduleKey: config.projectIntegration.key,
393
+ moduleKey: 'settings-api',
394
394
  }), (0, integration_credentials_1.default)(config, config.projectIntegration), (0, settings_1.default)());
395
395
  /**
396
396
  * @openapi
@@ -413,7 +413,7 @@ function addDefaultApiEndpoints(app, config) {
413
413
  config,
414
414
  optional: false,
415
415
  checkSubscriptionExpiration: true,
416
- moduleKey: config.projectIntegration.key,
416
+ moduleKey: 'settings-update-api',
417
417
  }), (0, integration_credentials_1.default)(config, config.projectIntegration), (0, settings_save_1.default)(config, config.projectIntegration));
418
418
  /**
419
419
  * @openapi
@@ -449,7 +449,7 @@ function addDefaultApiEndpoints(app, config) {
449
449
  config,
450
450
  optional: false,
451
451
  checkSubscriptionExpiration: true,
452
- moduleKey: config.projectIntegration.key,
452
+ moduleKey: 'sync-settings-api',
453
453
  }), (0, integration_credentials_1.default)(config, config.projectIntegration), (0, sync_settings_1.default)());
454
454
  /**
455
455
  * @openapi
@@ -472,7 +472,7 @@ function addDefaultApiEndpoints(app, config) {
472
472
  config,
473
473
  optional: false,
474
474
  checkSubscriptionExpiration: true,
475
- moduleKey: config.projectIntegration.key,
475
+ moduleKey: 'sync-settings-update-api',
476
476
  }), (0, integration_credentials_1.default)(config, config.projectIntegration), (0, sync_settings_save_1.default)(config, config.projectIntegration));
477
477
  if (config.projectIntegration.loginForm) {
478
478
  /**
@@ -497,7 +497,7 @@ function addDefaultApiEndpoints(app, config) {
497
497
  config,
498
498
  optional: false,
499
499
  checkSubscriptionExpiration: true,
500
- moduleKey: config.projectIntegration.key,
500
+ moduleKey: 'login-data',
501
501
  }), (req, res) => {
502
502
  var _a, _b;
503
503
  let fields = [];
@@ -527,7 +527,7 @@ function addDefaultApiEndpoints(app, config) {
527
527
  config,
528
528
  optional: false,
529
529
  checkSubscriptionExpiration: false,
530
- moduleKey: config.projectIntegration.key,
530
+ moduleKey: 'login',
531
531
  }), (0, integration_login_1.default)(config, config.projectIntegration));
532
532
  }
533
533
  }
@@ -77,8 +77,8 @@ function handle(config, integration) {
77
77
  options.zenModeUrl = parentUrl.toString();
78
78
  }
79
79
  const configurationFields = yield integration.getConfiguration(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials);
80
- options.configurationFields = configurationFields;
81
- logger(`Adding configuration fields ${JSON.stringify(configurationFields, null, 2)}`);
80
+ options.configurationFields = (0, defaults_1.groupFieldsByCategory)(configurationFields);
81
+ logger(`Adding configuration fields ${JSON.stringify(options.configurationFields, null, 2)}`);
82
82
  }
83
83
  options.infoModal = integration.infoModal;
84
84
  options.syncNewElements = integration.syncNewElements;
@@ -327,27 +327,23 @@ export interface OAuthLogin {
327
327
  */
328
328
  performRefreshTokenRequest?: (currentCredentials: any, loginForm?: any) => Promise<any>;
329
329
  }
330
- export interface File {
330
+ export interface BaseTreeItem {
331
331
  id: string;
332
332
  name: string;
333
- type: SourceFilesModel.FileType;
334
333
  parentId?: string;
335
334
  nodeType?: IntegrationTreeElementType;
336
335
  customContent?: string;
337
336
  labels?: LabelTreeElement[];
338
- failed?: boolean;
339
- excludedTargetLanguages?: string[];
337
+ disabled?: boolean;
338
+ tooltip?: string;
340
339
  path?: string;
341
340
  }
342
- export interface Folder {
343
- id: string;
344
- name: string;
345
- parentId?: string;
346
- nodeType?: IntegrationTreeElementType;
347
- customContent?: string;
348
- labels?: LabelTreeElement[];
349
- path?: string;
341
+ export interface File extends BaseTreeItem {
342
+ type: SourceFilesModel.FileType;
343
+ failed?: boolean;
344
+ excludedTargetLanguages?: string[];
350
345
  }
346
+ export type Folder = BaseTreeItem;
351
347
  export type TreeItem = File | Folder;
352
348
  /**
353
349
  * 0 - folder
@@ -359,6 +355,7 @@ export type FormEntity = FormField | FormDelimiter;
359
355
  export interface FormDelimiter {
360
356
  label?: string;
361
357
  labelHtml?: string;
358
+ category?: string;
362
359
  }
363
360
  export interface FormField {
364
361
  key: string;
@@ -398,6 +395,8 @@ export interface FormField {
398
395
  * only for notice type
399
396
  */
400
397
  noIcon?: boolean;
398
+ category?: string;
399
+ position?: number;
401
400
  }
402
401
  type NoticeType = 'info' | 'warning' | 'danger' | 'success' | 'error' | 'dataLostWarning';
403
402
  export interface IntegrationCredentials {
@@ -525,4 +524,8 @@ interface LoginFormTokenResponse {
525
524
  expires_in: number;
526
525
  refresh_token: string;
527
526
  }
527
+ export declare enum DefaultCategory {
528
+ GENERAL = "General Settings",
529
+ SYNC = "Sync settings"
530
+ }
528
531
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SyncSchedule = exports.SyncCondition = exports.Provider = void 0;
3
+ exports.DefaultCategory = exports.SyncSchedule = exports.SyncCondition = exports.Provider = void 0;
4
4
  var Provider;
5
5
  (function (Provider) {
6
6
  Provider["CROWDIN"] = "crowdin";
@@ -17,3 +17,8 @@ var SyncSchedule;
17
17
  SyncSchedule[SyncSchedule["DISABLED"] = 0] = "DISABLED";
18
18
  SyncSchedule[SyncSchedule["ACTIVE"] = 1 || 3 || 6 || 12 || 24] = "ACTIVE";
19
19
  })(SyncSchedule = exports.SyncSchedule || (exports.SyncSchedule = {}));
20
+ var DefaultCategory;
21
+ (function (DefaultCategory) {
22
+ DefaultCategory["GENERAL"] = "General Settings";
23
+ DefaultCategory["SYNC"] = "Sync settings";
24
+ })(DefaultCategory = exports.DefaultCategory || (exports.DefaultCategory = {}));
@@ -1,6 +1,6 @@
1
1
  import Crowdin, { SourceFilesModel } from '@crowdin/crowdin-api-client';
2
2
  import { Config } from '../../../types';
3
- import { IntegrationLogic } from '../types';
3
+ import { FormEntity, FormField, IntegrationLogic } from '../types';
4
4
  export declare function getRootFolder(config: Config, integration: IntegrationLogic, client: Crowdin, projectId: number): Promise<SourceFilesModel.Directory | undefined>;
5
5
  export declare function getOauthRoute(integration: IntegrationLogic): string;
6
6
  export declare function applyIntegrationModuleDefaults(config: Config, integration: IntegrationLogic): void;
@@ -12,3 +12,7 @@ export declare function constructOauthUrl({ config, integration, clientId, login
12
12
  }): string | undefined;
13
13
  export declare function getOAuthPollingId(clientId: string): string;
14
14
  export declare function getOAuthLoginFormId(clientId: string): string;
15
+ export declare function groupFieldsByCategory(fields: FormEntity[]): {
16
+ name: string;
17
+ fields: FormField[];
18
+ }[];
@@ -32,8 +32,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
32
32
  });
33
33
  };
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.getOAuthLoginFormId = exports.getOAuthPollingId = exports.constructOauthUrl = exports.applyIntegrationModuleDefaults = exports.getOauthRoute = exports.getRootFolder = void 0;
35
+ exports.groupFieldsByCategory = exports.getOAuthLoginFormId = exports.getOAuthPollingId = exports.constructOauthUrl = exports.applyIntegrationModuleDefaults = exports.getOauthRoute = exports.getRootFolder = void 0;
36
36
  const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
37
+ const types_1 = require("../types");
37
38
  function getRootFolder(config, integration, client, projectId) {
38
39
  return __awaiter(this, void 0, void 0, function* () {
39
40
  if (!integration.withRootFolder) {
@@ -158,6 +159,7 @@ function applyIntegrationModuleDefaults(config, integration) {
158
159
  label: 'Show In-Context Pseudo Language',
159
160
  type: 'checkbox',
160
161
  defaultValue: 'false',
162
+ category: types_1.DefaultCategory.GENERAL,
161
163
  });
162
164
  }
163
165
  if (integration.withCronSync || integration.webhooks) {
@@ -167,6 +169,8 @@ function applyIntegrationModuleDefaults(config, integration) {
167
169
  helpText: `Defines how often content is synced between ${config.name} and Crowdin. Make sure Auto Sync is enabled for selected directories and files in the dual pane view.`,
168
170
  type: 'select',
169
171
  defaultValue: '0',
172
+ category: types_1.DefaultCategory.SYNC,
173
+ position: 0,
170
174
  options: [
171
175
  {
172
176
  value: '0',
@@ -200,6 +204,8 @@ function applyIntegrationModuleDefaults(config, integration) {
200
204
  label: 'Automatically sync new translations from Crowdin',
201
205
  type: 'checkbox',
202
206
  dependencySettings: JSON.stringify([{ '#schedule-settings': { type: '!equal', value: ['0'] } }]),
207
+ category: types_1.DefaultCategory.SYNC,
208
+ position: 1,
203
209
  });
204
210
  }
205
211
  if ((_o = integration.syncNewElements) === null || _o === void 0 ? void 0 : _o.integration) {
@@ -208,6 +214,8 @@ function applyIntegrationModuleDefaults(config, integration) {
208
214
  label: `Automatically sync new content from ${config.name}`,
209
215
  type: 'checkbox',
210
216
  dependencySettings: JSON.stringify([{ '#schedule-settings': { type: '!equal', value: ['0'] } }]),
217
+ category: types_1.DefaultCategory.SYNC,
218
+ position: 2,
211
219
  });
212
220
  }
213
221
  if (integration.uploadTranslations) {
@@ -217,14 +225,20 @@ function applyIntegrationModuleDefaults(config, integration) {
217
225
  key: 'importEqSuggestions',
218
226
  label: 'Add translations that are the same as the source text',
219
227
  type: 'checkbox',
228
+ category: types_1.DefaultCategory.SYNC,
229
+ position: 3,
220
230
  }, {
221
231
  key: 'autoApproveImported',
222
232
  label: 'Mark added translations as Approved in Crowdin',
223
233
  type: 'checkbox',
234
+ category: types_1.DefaultCategory.SYNC,
235
+ position: 4,
224
236
  }, {
225
237
  key: 'translateHidden',
226
238
  label: 'Add translations for hidden source strings in Crowdin',
227
239
  type: 'checkbox',
240
+ category: types_1.DefaultCategory.SYNC,
241
+ position: 5,
228
242
  });
229
243
  }
230
244
  defaultSettings.push({
@@ -233,6 +247,8 @@ function applyIntegrationModuleDefaults(config, integration) {
233
247
  type: 'select',
234
248
  defaultValue: '0',
235
249
  dependencySettings: JSON.stringify([{ '#schedule-settings': { type: '!equal', value: ['0'] } }]),
250
+ category: types_1.DefaultCategory.SYNC,
251
+ position: 6,
236
252
  options: [
237
253
  {
238
254
  value: '0',
@@ -366,3 +382,38 @@ function getOAuthLoginFormId(clientId) {
366
382
  return `oauth_form_${clientId}`;
367
383
  }
368
384
  exports.getOAuthLoginFormId = getOAuthLoginFormId;
385
+ function groupFieldsByCategory(fields) {
386
+ const groupedFields = fields.reduce((acc, field) => {
387
+ if ('key' in field) {
388
+ const category = field.category || types_1.DefaultCategory.GENERAL;
389
+ if (!acc[category]) {
390
+ acc[category] = [];
391
+ }
392
+ acc[category].push(field);
393
+ }
394
+ return acc;
395
+ }, {});
396
+ // Sort fields by position within each category
397
+ Object.keys(groupedFields).forEach((category) => {
398
+ groupedFields[category].sort((a, b) => {
399
+ // If neither has position, maintain original order
400
+ if (!('position' in a) && !('position' in b)) {
401
+ return 0;
402
+ }
403
+ // If only one has position, the one without position goes to the end
404
+ if (!('position' in a)) {
405
+ return 1;
406
+ }
407
+ if (!('position' in b)) {
408
+ return -1;
409
+ }
410
+ // If both have position, sort by position value (lower numbers first)
411
+ return (a.position || 0) - (b.position || 0);
412
+ });
413
+ });
414
+ return Object.entries(groupedFields).map(([category, fields]) => ({
415
+ name: category,
416
+ fields,
417
+ }));
418
+ }
419
+ exports.groupFieldsByCategory = groupFieldsByCategory;
@@ -69,7 +69,7 @@ function webhookHandler(config, webhooks) {
69
69
  const json = JSON.parse(req.body.toString());
70
70
  for (const webhook of webhooks) {
71
71
  if (webhook.key === moduleKey) {
72
- yield webhook.callback({ credentials, events: json.events, client });
72
+ yield webhook.callback({ events: json.events, client });
73
73
  }
74
74
  }
75
75
  }));
@@ -1,4 +1,4 @@
1
- import { CrowdinCredentials, ModuleKey } from '../../types';
1
+ import { ModuleKey } from '../../types';
2
2
  import Crowdin from '@crowdin/crowdin-api-client';
3
3
  export interface Webhook extends ModuleKey {
4
4
  /**
@@ -9,7 +9,6 @@ export interface Webhook extends ModuleKey {
9
9
  * handle function
10
10
  */
11
11
  callback: (data: {
12
- credentials: CrowdinCredentials;
13
12
  events: Event[];
14
13
  client: Crowdin;
15
14
  }) => Promise<void>;
@@ -39,5 +39,8 @@ exports.engine = (0, express_handlebars_1.default)({
39
39
  or: function (a, b, options) {
40
40
  return a || b ? options.fn(this) : options.inverse(this);
41
41
  },
42
+ incrementedIndex: function (index) {
43
+ return index + 1;
44
+ },
42
45
  },
43
46
  });
@@ -249,7 +249,6 @@
249
249
  <crowdin-modal
250
250
  style="display: none;"
251
251
  id="settings-modal"
252
- body-overflow-unset="{{#checkLength configurationFields 3}}false{{else}}true{{/checkLength}}"
253
252
  modal-width="65"
254
253
  modal-title="Settings"
255
254
  close-button-title="Close"
@@ -258,7 +257,11 @@
258
257
  <crowdin-progress-indicator></crowdin-progress-indicator>
259
258
  </div>
260
259
  <div id="modal-content">
260
+ <crowdin-tabs tabs-config='[{{#each configurationFields}}"{{name}}"{{#unless @last}},{{/unless}}{{/each}}]' active-tab="1">
261
261
  {{#each configurationFields}}
262
+ <div slot="tab-{{incrementedIndex @index}}">
263
+ <div class="crowdin-form">
264
+ {{#each fields}}
262
265
  {{#if key}}
263
266
  {{#ifeq type "checkbox"}}
264
267
  <crowdin-checkbox
@@ -395,7 +398,11 @@
395
398
  data-dependency="{{dependencySettings}}"
396
399
  {{/if}}
397
400
  ></div>
401
+ {{/each}}
402
+ </div>
403
+ </div>
398
404
  {{/each}}
405
+ </crowdin-tabs>
399
406
  </div>
400
407
  <div slot="footer">
401
408
  <crowdin-button id="settings-save-btn" outlined onclick="saveSettings()">Save</crowdin-button>
@@ -529,6 +536,8 @@
529
536
  id: e.id,
530
537
  customContent: e.customContent,
531
538
  labels: e.labels,
539
+ disabled: e.disabled,
540
+ tooltip: e.tooltip,
532
541
  };
533
542
  if (e.type) {
534
543
  item.type = e.type;
@@ -584,6 +593,8 @@
584
593
  id: e.id,
585
594
  customContent: e.customContent,
586
595
  labels: e.labels,
596
+ disabled: e.disabled,
597
+ tooltip: e.tooltip,
587
598
  };
588
599
  if (e.type) {
589
600
  item.isNew = e.isNew;
@@ -1178,26 +1189,31 @@
1178
1189
 
1179
1190
  function saveSettings() {
1180
1191
  setLoader('#settings-modal');
1181
- const settingsElements = Array.from(document.getElementById('modal-content').children);
1192
+ const settingsElements = Array.from(document.querySelectorAll('#modal-content crowdin-tabs > div'));
1182
1193
  const tags = ['crowdin-checkbox', 'crowdin-select', 'crowdin-input', 'crowdin-textarea'];
1183
1194
  const configReq = {};
1184
- settingsElements
1185
- .filter(e => tags.includes(e.tagName.toLowerCase()))
1186
- .forEach(e => {
1187
- const key = e.getAttribute('key');
1188
- let value;
1189
- if (e.tagName.toLowerCase() === 'crowdin-select') {
1190
- value = JSON.parse(e.value);
1191
- if (!e.hasAttribute('is-multi')) {
1192
- value = value.length > 0 ? value[0] : undefined;
1195
+
1196
+ settingsElements.forEach(tab => {
1197
+ const formElements = Array.from(tab.querySelectorAll('.crowdin-form > *'));
1198
+ formElements
1199
+ .filter(e => tags.includes(e.tagName.toLowerCase()))
1200
+ .forEach(e => {
1201
+ const key = e.getAttribute('key');
1202
+ let value;
1203
+ if (e.tagName.toLowerCase() === 'crowdin-select') {
1204
+ value = JSON.parse(e.value);
1205
+ if (!e.hasAttribute('is-multi')) {
1206
+ value = value.length > 0 ? value[0] : undefined;
1207
+ }
1208
+ } else if (e.tagName.toLowerCase() === 'crowdin-checkbox') {
1209
+ value = !!e.checked;
1210
+ } else {
1211
+ value = e.value;
1193
1212
  }
1194
- } else if (e.tagName.toLowerCase() === 'crowdin-checkbox') {
1195
- value = !!e.checked;
1196
- } else {
1197
- value = e.value;
1198
- }
1199
- configReq[key] = value;
1200
- });
1213
+ configReq[key] = value;
1214
+ });
1215
+ });
1216
+
1201
1217
  settingsSaveBtn.setAttribute('disabled', true);
1202
1218
  checkOrigin()
1203
1219
  .then(restParams => fetch('api/settings' + restParams, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.87.0",
3
+ "version": "0.88.1",
4
4
  "description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
5
5
  "main": "out/index.js",
6
6
  "types": "out/index.d.ts",