@crowdin/app-project-module 2.3.0 → 2.4.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.
@@ -875,7 +875,29 @@
875
875
  return values.filter((value) => value !== '');
876
876
  }
877
877
 
878
- const EMPTY_PERMISSION_VALUE = '\u2015';
878
+ const IntegrationView = ({ appComponentRef, userErrorsTableRef, name, isErrorLogsVisible, syncNewElements, withCronSync, webhooks, integrationOneLevelFetching, integrationSearchListener, integrationPagination, filtering, integrationButtonMenuItems, crowdinButtonMenuItems, }) => {
879
+ var _a, _b;
880
+ return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("div", { style: { display: isErrorLogsVisible ? 'none' : undefined }, children: jsxRuntimeExports.jsx("crowdin-simple-integration", { ref: appComponentRef, "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", ...(((_a = integrationButtonMenuItems === null || integrationButtonMenuItems === void 0 ? void 0 : integrationButtonMenuItems.length) !== null && _a !== void 0 ? _a : 0) > 0
881
+ ? { 'integration-button-menu-items': integrationButtonMenuItems }
882
+ : {}), ...(((_b = crowdinButtonMenuItems === null || crowdinButtonMenuItems === void 0 ? void 0 : crowdinButtonMenuItems.length) !== null && _b !== void 0 ? _b : 0) > 0
883
+ ? { 'crowdin-button-menu-items': crowdinButtonMenuItems }
884
+ : {}), ...((filtering === null || filtering === void 0 ? void 0 : filtering.crowdinLanguages) ? { 'crowdin-filter': true } : {}), ...((filtering === null || filtering === void 0 ? void 0 : filtering.integrationFilterConfig)
885
+ ? {
886
+ 'integration-filter': true,
887
+ 'integration-filter-config': filtering.integrationFilterConfig,
888
+ }
889
+ : {}) }) }), jsxRuntimeExports.jsxs("div", { id: "user-errors", style: { display: isErrorLogsVisible ? undefined : 'none' }, children: [jsxRuntimeExports.jsx("crowdin-show-as-table", { ref: userErrorsTableRef, "is-loading": true, id: "user-errors-table", "is-searchable": true, "total-records": "25", "search-placeholder": "Search something" }), jsxRuntimeExports.jsx("crowdin-alert", { children: "This table displays the most recent error logs from the past month. Logs older than one month will be automatically deleted." })] })] }));
890
+ };
891
+
892
+ const TopBar = ({ notice, isNoticeVisible, checkSubscription, subscriptionInfo, isErrorLogsVisible, isManager, infoModal, hasSettings, isOwner, onCloseNotice, onShowIntegration, onShowErrorLogs, onShowPermissions, onOpenInfoModal, onOpenSettings, onLogout, }) => {
893
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-2.5 text-right", children: [notice && (jsxRuntimeExports.jsxs("crowdin-alert", { id: "notice", title: notice.title, ...(!notice.icon ? { 'no-icon': 'true' } : {}), ...(notice.type ? { type: notice.type } : {}), style: { display: isNoticeVisible ? 'block' : 'none', marginBottom: '12px' }, children: [jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center", children: jsxRuntimeExports.jsx("p", { className: "m-0", dangerouslySetInnerHTML: { __html: notice.content } }) }), notice.close && (jsxRuntimeExports.jsx("crowdin-button", { onClick: () => onCloseNotice('notice'), class: "absolute top-0 right-0", icon: true, children: "close" }))] })), checkSubscription && (jsxRuntimeExports.jsx("crowdin-alert", { id: "subscription-info", "no-icon": "true", type: "warning", style: { display: subscriptionInfo ? 'block' : 'none', marginBottom: '12px' }, children: jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-center", children: [jsxRuntimeExports.jsx("p", { className: "m-0", children: subscriptionInfo ? `Your trial expires in ${subscriptionInfo.daysLeft} days.` : '' }), jsxRuntimeExports.jsx("crowdin-button", { class: "ml-2", primary: true, onClick: () => {
894
+ if (subscriptionInfo === null || subscriptionInfo === void 0 ? void 0 : subscriptionInfo.subscribeLink) {
895
+ window.open(subscriptionInfo.subscribeLink, '_blank', 'noopener,noreferrer');
896
+ }
897
+ }, children: "Subscribe" })] }) })), jsxRuntimeExports.jsxs("div", { id: "buttons", className: "flex w-full items-center justify-between", children: [jsxRuntimeExports.jsxs("div", { className: "mr-auto", children: [jsxRuntimeExports.jsx("span", { style: { display: isErrorLogsVisible ? undefined : 'none' }, children: jsxRuntimeExports.jsx("crowdin-button", { id: "show-integration-btn", "icon-before": "arrow_back", onClick: onShowIntegration, children: "Integration" }) }), jsxRuntimeExports.jsx("span", { style: { display: isErrorLogsVisible ? 'none' : undefined }, children: jsxRuntimeExports.jsx("crowdin-button", { id: "show-error-logs-btn", "icon-before": "list", onClick: onShowErrorLogs, children: "Error logs" }) })] }), isManager && (jsxRuntimeExports.jsx("crowdin-button", { "icon-before": "link", onClick: onShowPermissions, children: "Share" })), infoModal && (jsxRuntimeExports.jsx("crowdin-button", { "icon-before": "info", onClick: onOpenInfoModal, children: infoModal.title })), hasSettings && (jsxRuntimeExports.jsx("crowdin-button", { "icon-before": "settings", onClick: onOpenSettings, children: "Settings" })), jsxRuntimeExports.jsx("crowdin-button", { ...(!isOwner ? { disabled: true, title: 'Only the owner can log out' } : {}), "icon-before": "account_circle", onClick: onLogout, children: "Log out" })] })] }));
898
+ };
899
+
900
+ const EMPTY_PERMISSION_VALUE = '―';
879
901
  const getDefaultScheduleModalState = () => ({
880
902
  type: null,
881
903
  showNewFiles: true,
@@ -885,1592 +907,1770 @@
885
907
  selectedFiles: true,
886
908
  allFolderFiles: false,
887
909
  });
888
- const renderConfigField = (field, settingsFieldChange, registerSettingsField, suffix = 'settings') => {
889
- var _a;
890
- if (!field.key) {
891
- return field.labelHtml ? (jsxRuntimeExports.jsx("crowdin-p", { ...(field.dependencySettings ? { 'data-dependency': field.dependencySettings } : {}), dangerouslySetInnerHTML: { __html: field.labelHtml } })) : (jsxRuntimeExports.jsx("crowdin-p", { ...(field.dependencySettings ? { 'data-dependency': field.dependencySettings } : {}), children: field.label }));
910
+ const JOB_TYPE = {
911
+ updateCrowdin: 'updateCrowdin',
912
+ updateIntegration: 'updateIntegration',
913
+ updateTargetLanguages: 'updateTargetLanguages',
914
+ integrationSyncSettingsSave: 'integrationSyncSettingsSave',
915
+ crowdinSyncSettingsSave: 'crowdinSyncSettingsSave',
916
+ integrationSettingsSave: 'integrationSettingsSave',
917
+ };
918
+ const JOB_STATUS = {
919
+ created: 'created',
920
+ inProgress: 'inProgress',
921
+ failed: 'failed',
922
+ canceled: 'canceled',
923
+ finished: 'finished',
924
+ };
925
+ const FOLDER_TYPE = '0';
926
+ const FILE_TYPE = '1';
927
+ const BRANCH_TYPE = '2';
928
+
929
+ function isFolder(e) {
930
+ return !e.type && (e.nodeType === undefined || e.nodeType === FOLDER_TYPE || e.nodeType === BRANCH_TYPE);
931
+ }
932
+ function openModal(modal) {
933
+ if (!modal) {
934
+ return;
892
935
  }
893
- const commonProps = {
894
- id: `${field.key}-${suffix}`,
895
- _key: field.key,
896
- label: field.label,
897
- ...(field.helpText ? { 'help-text': field.helpText } : {}),
898
- ...(field.helpTextHtml ? { 'help-text-html': field.helpTextHtml } : {}),
899
- ...(field.dependencySettings ? { 'data-dependency': field.dependencySettings } : {}),
900
- };
901
- switch (field.type) {
902
- case 'checkbox':
903
- return (jsxRuntimeExports.jsx("crowdin-checkbox", { ref: registerSettingsField(field.key), ...commonProps, value: "false", "use-switch": true, ...(field.defaultValue === true ? { checked: String(field.defaultValue) } : {}), onChange: settingsFieldChange }));
904
- case 'select':
905
- return (jsxRuntimeExports.jsx("crowdin-select", { ref: registerSettingsField(field.key), ...commonProps, ...(field.isMulti ? { 'is-multi': true, 'close-on-select': 'false' } : {}), ...(field.isSearchable ? { 'is-searchable': true } : {}), "is-position-fixed": true, onChange: settingsFieldChange, children: (_a = field.options) === null || _a === void 0 ? void 0 : _a.map((option) => {
906
- const isSelected = Array.isArray(field.defaultValue)
907
- ? field.defaultValue.includes(option.value)
908
- : field.defaultValue === option.value;
909
- return (jsxRuntimeExports.jsx("option", { value: option.value, ...(isSelected ? { selected: true } : {}), children: option.label }, option.value));
910
- }) }));
911
- case 'text':
912
- return (jsxRuntimeExports.jsx("crowdin-input", { ref: registerSettingsField(field.key), ...commonProps, "with-fixed-height": true, value: field.defaultValue || '', onChange: settingsFieldChange }));
913
- case 'textarea':
914
- return (jsxRuntimeExports.jsx("crowdin-textarea", { ref: registerSettingsField(field.key), ...commonProps, value: field.defaultValue || '', onChange: settingsFieldChange }));
915
- case 'notice':
916
- return (jsxRuntimeExports.jsx("crowdin-alert", { ...(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.helpTextHtml || field.helpText || '' } }));
917
- default:
918
- return null;
936
+ modal.style.display = 'block';
937
+ modal.open();
938
+ }
939
+ function closeModal(modal) {
940
+ if (!modal) {
941
+ return;
919
942
  }
920
- };
921
- const MainPage = (props) => {
922
- var _a, _b;
923
- const { name, userId, notice, checkSubscription, isManager, isOwner, infoModal, configurationFields, config: configString, syncNewElements, withCronSync, webhooks, integrationOneLevelFetching, integrationSearchListener, integrationPagination, excludedTargetLanguages, filtering, hasOrganization, zenModeUrl, asyncProgress = { checkInterval: 3000 }, reloadOnConfigSave, progressiveCrowdinFilesLoading, integrationButtonMenuItems, crowdinButtonMenuItems, } = props;
924
- const config = reactExports.useRef(configString ? JSON.parse(configString.replace(/\\n/g, '\\\\n')) : {});
925
- const folderType = '0';
926
- const fileType = '1';
927
- const branchType = '2';
928
- const JOB_TYPE = {
929
- updateCrowdin: 'updateCrowdin',
930
- updateIntegration: 'updateIntegration',
931
- updateTargetLanguages: 'updateTargetLanguages',
932
- integrationSyncSettingsSave: 'integrationSyncSettingsSave',
933
- crowdinSyncSettingsSave: 'crowdinSyncSettingsSave',
934
- integrationSettingsSave: 'integrationSettingsSave',
935
- };
936
- const JOB_STATUS = {
937
- created: 'created',
938
- inProgress: 'inProgress',
939
- failed: 'failed',
940
- canceled: 'canceled',
941
- finished: 'finished',
942
- };
943
- const silentJobs = [JOB_TYPE.integrationSettingsSave];
944
- const appComponent = reactExports.useRef(null);
945
- const asyncProgressRef = reactExports.useRef(null);
946
- const infoModalRef = reactExports.useRef(null);
947
- const subscriptionModal = reactExports.useRef(null);
948
- const settingsModalRef = reactExports.useRef(null);
949
- const scheduleModalRef = reactExports.useRef(null);
950
- const selectedFilesCheckboxRef = reactExports.useRef(null);
951
- const allFolderFilesCheckboxRef = reactExports.useRef(null);
952
- const newFilesCheckboxRef = reactExports.useRef(null);
953
- const languageModal = reactExports.useRef(null);
954
- const languageSelectRef = reactExports.useRef(null);
955
- const permissionsModalRef = reactExports.useRef(null);
956
- const userErrorsTableRef = reactExports.useRef(null);
957
- const userErrorDetailModalRef = reactExports.useRef(null);
943
+ modal.style.display = 'none';
944
+ modal.close();
945
+ }
946
+
947
+ /// <reference path="../../../../ui/types.d.ts" />
948
+ function usePermissions({ userId, hasOrganization, permissionsModalRef }) {
958
949
  const usersSelectRef = reactExports.useRef(null);
959
- const settingsFieldRefs = reactExports.useRef({});
960
- const refreshDependencies = useFieldDependencies(configurationFields);
961
- const crowdinFilesToUpdateTargetLanguages = reactExports.useRef([]);
962
- const asyncJobs = reactExports.useRef({});
963
- const project = reactExports.useRef({});
964
- const crowdinData = reactExports.useRef([]);
965
- const fileToSync = reactExports.useRef([]);
966
- const syncData = reactExports.useRef(null);
967
- const subscriptionLink = reactExports.useRef('');
968
- const hasLoadMoreElements = reactExports.useRef(false);
969
- const savedIntegrationData = reactExports.useRef(null);
970
- const currentOpenedFolders = reactExports.useRef(null);
971
- const [modalLoaders, setModalLoaders] = reactExports.useState({});
972
- const [languageModalState, setLanguageModalState] = reactExports.useState(null);
950
+ const [isLoading, setIsLoading] = reactExports.useState(false);
973
951
  const [permissionUserOptions, setPermissionUserOptions] = reactExports.useState([]);
974
952
  const [permissionsSelectedValue, setPermissionsSelectedValue] = reactExports.useState('[]');
975
953
  const [permissionsSummary, setPermissionsSummary] = reactExports.useState(null);
976
954
  const [isPermissionsConfirmVisible, setIsPermissionsConfirmVisible] = reactExports.useState(false);
977
- const [isNoticeVisible, setIsNoticeVisible] = reactExports.useState(false);
978
- const [isSettingsSaveDisabled, setIsSettingsSaveDisabled] = reactExports.useState(false);
979
- const [scheduleModalState, setScheduleModalState] = reactExports.useState(getDefaultScheduleModalState);
980
- const [isScheduleSaveDisabled, setIsScheduleSaveDisabled] = reactExports.useState(false);
981
- const [subscriptionInfo, setSubscriptionInfo] = reactExports.useState(null);
982
- const [isErrorLogsVisible, setIsErrorLogsVisible] = reactExports.useState(false);
983
- const [selectedUserError, setSelectedUserError] = reactExports.useState(null);
984
- reactExports.useEffect(() => {
985
- // Event listeners
986
- const listeners = {
987
- refreshFilesList: (e) => {
988
- if (e.detail.refreshIntegration) {
989
- getIntegrationData({ hardReload: true });
990
- }
991
- else if (e.detail.refreshCrowdin) {
992
- getCrowdinData();
993
- }
994
- },
995
- crowdinFilesFolderToggled: (event) => {
996
- if (event.detail.componentId === 'crowdin-files' && event.detail.isOpen && event.detail.type === '1') {
997
- getFileProgress(event.detail.id);
998
- }
999
- if (integrationOneLevelFetching) {
1000
- if (event.detail.componentId === 'integration-files' && event.detail.isOpen) {
1001
- getIntegrationData({ parentId: event.detail.id, recursion: event.detail.recursion });
1002
- }
1003
- }
1004
- },
1005
- uploadFilesToCrowdin: uploadFilesToCrowdin,
1006
- uploadFilesToIntegration: uploadFilesToIntegration,
1007
- cancelAsyncAction: (e) => {
1008
- cancelJob(e.detail);
1009
- },
1010
- keydown: (event) => {
1011
- if (event.keyCode === 27) {
1012
- closeModal(permissionsModalRef.current);
1013
- closeModal(infoModalRef.current);
1014
- closeModal(settingsModalRef.current);
1015
- closeModal(scheduleModalRef.current);
1016
- if (languageModal.current) {
1017
- closeModal(languageModal.current);
1018
- }
1019
- }
1020
- },
1021
- loadMoreClick: async (e) => {
1022
- var _a;
1023
- const loadMoreFile = e.detail;
1024
- const fileComponent = (_a = appComponent.current) === null || _a === void 0 ? void 0 : _a.querySelector('#integration-files');
1025
- if (!fileComponent) {
1026
- return;
1027
- }
1028
- await fileComponent.setLoadMoreLoading(loadMoreFile.id, true);
1029
- getIntegrationData({ parentId: e.detail.parent_id, paginationData: e.detail.pagination }).finally(async () => {
1030
- await fileComponent.setLoadMoreLoading(loadMoreFile.id, false);
1031
- });
1032
- },
1033
- };
1034
- if (integrationSearchListener) {
1035
- listeners['integrationFilterChange'] = async (event) => {
1036
- const isEmptySearch = !event.detail || event.detail.trim() === '';
1037
- if (isEmptySearch && savedIntegrationData.current && hasLoadMoreElements.current) {
1038
- appComponent.current.setIntegrationFilesData(savedIntegrationData.current);
1039
- appComponent.current.setIntegrationOpenedFolders(currentOpenedFolders.current);
1040
- return;
1041
- }
1042
- if (!isEmptySearch && hasLoadMoreElements.current) {
1043
- savedIntegrationData.current = await appComponent.current.getIntegrationFilesData();
1044
- currentOpenedFolders.current = await appComponent.current.getIntegrationOpenedFolders();
1045
- }
1046
- getIntegrationData({ search: event.detail });
1047
- };
1048
- }
1049
- if (integrationPagination) {
1050
- listeners['fileListEnd'] = (e) => {
1051
- getIntegrationData({ page: e.detail.page });
1052
- };
1053
- }
1054
- if (withCronSync || webhooks) {
1055
- listeners['integrationScheduleSync'] = setIntegrationScheduleSync;
1056
- listeners['crowdinScheduleSync'] = setCrowdinScheduleSync;
1057
- listeners['crowdinDisableSync'] = disableCrowdinSync;
1058
- listeners['integrationDisableSync'] = disableIntegrationSync;
1059
- }
1060
- for (const [event, handler] of Object.entries(listeners)) {
1061
- document.body.addEventListener(event, handler);
1062
- }
1063
- // Initial data fetch
1064
- getCrowdinData();
1065
- getIntegrationData({});
1066
- getActiveJobs();
1067
- if (checkSubscription) {
1068
- getSubscriptionInfo();
1069
- }
1070
- setIsNoticeVisible(Boolean(notice) && localStorage.getItem('revised_notice') !== '1');
1071
- return () => {
1072
- for (const [event, handler] of Object.entries(listeners)) {
1073
- document.body.removeEventListener(event, handler);
1074
- }
1075
- };
1076
- }, []);
1077
- reactExports.useEffect(() => {
1078
- var _a;
1079
- if (!languageModalState || !((_a = languageSelectRef.current) === null || _a === void 0 ? void 0 : _a.setValue)) {
1080
- return;
1081
- }
1082
- languageSelectRef.current.setValue(languageModalState.languages.map((language) => language.id));
1083
- }, [languageModalState]);
1084
955
  reactExports.useEffect(() => {
1085
956
  if (usersSelectRef.current) {
1086
957
  usersSelectRef.current.value = permissionsSelectedValue;
1087
958
  }
1088
959
  }, [permissionUserOptions, permissionsSelectedValue]);
1089
- reactExports.useEffect(() => {
1090
- syncScheduleCheckbox(selectedFilesCheckboxRef.current, {
1091
- checked: scheduleModalState.selectedFiles,
1092
- });
1093
- syncScheduleCheckbox(allFolderFilesCheckboxRef.current, {
1094
- checked: scheduleModalState.allFolderFiles,
1095
- hidden: !scheduleModalState.showAllFolderFiles,
1096
- });
1097
- syncScheduleCheckbox(newFilesCheckboxRef.current, {
1098
- checked: scheduleModalState.newFiles,
1099
- hidden: !scheduleModalState.showNewFiles,
1100
- clearSelection: scheduleModalState.newFilesClearSelection,
1101
- });
1102
- }, [scheduleModalState]);
1103
- // Expose centralized API to global scope for main.js compatibility
1104
- reactExports.useEffect(() => {
1105
- window.app = {
1106
- subscriptionModal: subscriptionModal,
1107
- subscriptionLink: subscriptionLink,
1108
- openModal: openModal,
1109
- };
1110
- return () => {
1111
- delete window.app;
1112
- };
1113
- }, []);
1114
960
  // crowdin-users-select doesn't trigger onChange reliably, using native event listener instead
1115
961
  reactExports.useEffect(() => {
1116
962
  const selectElement = usersSelectRef.current;
1117
- if (selectElement) {
1118
- const handleChange = () => {
1119
- checkUsers();
1120
- };
1121
- selectElement.addEventListener('change', handleChange);
1122
- return () => {
1123
- selectElement.removeEventListener('change', handleChange);
1124
- };
963
+ if (!selectElement) {
964
+ return;
1125
965
  }
966
+ const handleChange = () => checkUsers();
967
+ selectElement.addEventListener('change', handleChange);
968
+ return () => selectElement.removeEventListener('change', handleChange);
1126
969
  }, []);
1127
- function integrationLogout() {
1128
- checkOrigin()
1129
- .then((queryParams) => fetch(`api/logout${queryParams}`, { method: 'POST' }))
1130
- .then(checkResponse)
1131
- .then(reloadLocation)
1132
- .then(() => localStorage.removeItem(`revised_${name}`))
1133
- .catch((e) => catchRejection(e, e.error || 'Looks like you are not logged in'));
970
+ function getUserFullName(user) {
971
+ if (user.full_name) {
972
+ return user.full_name;
973
+ }
974
+ const ownerFullName = user.name || ((user.first_name || '') + ' ' + (user.last_name || '')).trim();
975
+ return !!ownerFullName && (user === null || user === void 0 ? void 0 : user.login) !== ownerFullName ? `${ownerFullName} (${user === null || user === void 0 ? void 0 : user.login})` : user === null || user === void 0 ? void 0 : user.login;
1134
976
  }
1135
- function transformCrowdinItemToTree(e) {
1136
- const item = {
1137
- parent_id: e.parentId ? e.parentId : '0',
1138
- name: e.name,
1139
- id: e.id,
1140
- customContent: e.customContent,
1141
- labels: e.labels,
1142
- disabled: e.disabled,
1143
- tooltip: e.tooltip,
1144
- };
1145
- if (e.type) {
1146
- item.type = e.type;
1147
- item.node_type = fileType;
1148
- item.failed = e.failed;
1149
- item.excludedTargetLanguages = e.excludedTargetLanguages;
977
+ function hideConfirmUsersBlock() {
978
+ var _a;
979
+ setIsPermissionsConfirmVisible(false);
980
+ (_a = permissionsModalRef.current) === null || _a === void 0 ? void 0 : _a.setAttribute('body-overflow-unset', true);
981
+ }
982
+ function showConfirmUsersBlock() {
983
+ var _a;
984
+ setIsPermissionsConfirmVisible(true);
985
+ (_a = permissionsModalRef.current) === null || _a === void 0 ? void 0 : _a.removeAttribute('body-overflow-unset');
986
+ }
987
+ async function showPermissionsDialog() {
988
+ hideConfirmUsersBlock();
989
+ openModal(permissionsModalRef.current);
990
+ setIsLoading(true);
991
+ setPermissionsSelectedValue('[]');
992
+ try {
993
+ const [managers, users] = await Promise.all([
994
+ new Promise((resolve) => {
995
+ checkOrigin()
996
+ .then((restParams) => fetch('api/users' + restParams))
997
+ .then(checkResponse)
998
+ .then((res) => resolve(JSON.stringify(res.data.managers)));
999
+ }),
1000
+ new Promise((resolve) => {
1001
+ AP.getUsers((res) => {
1002
+ const users = Object.values(res).filter((user) => user.id !== Number(userId));
1003
+ resolve(users);
1004
+ });
1005
+ }),
1006
+ ]);
1007
+ const parsedManagers = (JSON.parse(managers) || []);
1008
+ const validUserIds = new Set(users.map((user) => `${user.id}`));
1009
+ const filteredManagers = parsedManagers.filter((id) => validUserIds.has(id));
1010
+ setPermissionUserOptions(users.map((user) => ({
1011
+ id: String(user.id),
1012
+ label: getUserFullName(user),
1013
+ })));
1014
+ setPermissionsSelectedValue(JSON.stringify(filteredManagers));
1150
1015
  }
1151
- else {
1152
- item.node_type = e.nodeType || folderType;
1016
+ catch (e) {
1017
+ catchRejection(e, "Can't fetch users");
1018
+ }
1019
+ finally {
1020
+ setTimeout(() => setIsLoading(false), 500);
1153
1021
  }
1154
- return item;
1155
1022
  }
1156
- function getCrowdinData() {
1157
- var _a;
1158
- (_a = appComponent.current) === null || _a === void 0 ? void 0 : _a.setAttribute('is-crowdin-loading', 'true');
1159
- let promise;
1160
- if (progressiveCrowdinFilesLoading) {
1161
- promise = checkOrigin()
1162
- .then((restParams) => {
1163
- const dirPromise = fetch('api/crowdin/files' + restParams + '&mode=directories')
1164
- .then(checkResponse)
1165
- .then((directories) => {
1166
- var _a;
1167
- const dirTree = directories.map(transformCrowdinItemToTree);
1168
- crowdinData.current = dirTree;
1169
- (_a = appComponent.current) === null || _a === void 0 ? void 0 : _a.setCrowdinFilesData(dirTree);
1170
- return dirTree;
1171
- });
1172
- const filesPromise = fetch('api/crowdin/files' + restParams + '&mode=files').then(checkResponse);
1173
- const projectPromise = fetch('api/crowdin/project' + restParams).then(checkResponse);
1174
- return Promise.all([dirPromise, filesPromise, projectPromise]);
1175
- })
1176
- .then(([dirTree, files, projectData]) => {
1177
- var _a, _b, _c;
1178
- const fileTree = files.map(transformCrowdinItemToTree);
1179
- crowdinData.current = [...dirTree, ...fileTree];
1180
- (_a = appComponent.current) === null || _a === void 0 ? void 0 : _a.setCrowdinFilesData(crowdinData.current);
1181
- project.current = projectData;
1182
- const languagesSorted = project.current.targetLanguages
1183
- .map((language) => ({
1184
- sourceLanguageId: projectData.sourceLanguage.editorCode,
1185
- projectEditorLink: projectData.projectEditorLink,
1186
- ...language,
1187
- }))
1188
- .sort((a, b) => a.name.localeCompare(b.name));
1189
- if (project.current.inContext && ((_b = config.current) === null || _b === void 0 ? void 0 : _b.inContext)) {
1190
- languagesSorted.push({ ...project.current.inContextPseudoLanguage, inContext: true });
1191
- }
1192
- setLanguagesForTranslation(languagesSorted);
1193
- (_c = appComponent.current) === null || _c === void 0 ? void 0 : _c.setCrowdinLanguagesData(languagesSorted);
1194
- });
1023
+ async function checkUsers() {
1024
+ setIsLoading(true);
1025
+ const select = usersSelectRef.current;
1026
+ if (select.value === '[]') {
1027
+ hideConfirmUsersBlock();
1028
+ setTimeout(() => setIsLoading(false), 500);
1029
+ return;
1195
1030
  }
1196
- else {
1197
- promise = checkOrigin()
1198
- .then((restParams) => {
1199
- const filesPromise = fetch('api/crowdin/files' + restParams).then(checkResponse);
1200
- const projectPromise = fetch('api/crowdin/project' + restParams).then(checkResponse);
1201
- return Promise.all([filesPromise, projectPromise]);
1202
- })
1203
- .then(([files, projectData]) => {
1204
- var _a;
1205
- const tree = files.map(transformCrowdinItemToTree);
1206
- crowdinData.current = tree;
1207
- appComponent.current.setCrowdinFilesData(tree);
1208
- project.current = projectData;
1209
- const languagesSorted = project.current.targetLanguages
1210
- .map((language) => ({
1211
- sourceLanguageId: projectData.sourceLanguage.editorCode,
1212
- projectEditorLink: projectData.projectEditorLink,
1213
- ...language,
1214
- }))
1215
- .sort((a, b) => a.name.localeCompare(b.name));
1216
- if (project.current.inContext && ((_a = config.current) === null || _a === void 0 ? void 0 : _a.inContext)) {
1217
- languagesSorted.push({ ...project.current.inContextPseudoLanguage, inContext: true });
1218
- }
1219
- setLanguagesForTranslation(languagesSorted);
1220
- appComponent.current.setCrowdinLanguagesData(languagesSorted);
1031
+ const users = JSON.parse(select.value);
1032
+ const user_ids = users.filter((id) => parseInt(id));
1033
+ const emails = users.filter((id) => !parseInt(id));
1034
+ try {
1035
+ const [appManagers, checkUsersResponse, allUsers] = await Promise.all([
1036
+ new Promise((resolve) => {
1037
+ checkOrigin()
1038
+ .then((restParams) => fetch('api/users' + restParams))
1039
+ .then(checkResponse)
1040
+ .then((usersResponse) => resolve(usersResponse.data.managers));
1041
+ }),
1042
+ new Promise((resolve) => {
1043
+ AP.validateUsers({ user_ids, emails }, (res) => resolve(res.data));
1044
+ }),
1045
+ new Promise((resolve) => {
1046
+ AP.getUsers((res) => resolve(res));
1047
+ }),
1048
+ ]);
1049
+ const newUserEmails = checkUsersResponse.newUserEmails.map((email) => ({ name: email }));
1050
+ const newProjectManagers = [
1051
+ ...newUserEmails,
1052
+ ...checkUsersResponse.newProjectManagers.map((user) => ({ name: getUserFullName(user) })),
1053
+ ];
1054
+ const newApplicationAccessUsers = [
1055
+ ...newUserEmails,
1056
+ ...checkUsersResponse.newApplicationAccessUsers.map((user) => ({
1057
+ name: getUserFullName(user),
1058
+ })),
1059
+ ];
1060
+ const newApplicationManagers = [
1061
+ ...newUserEmails,
1062
+ ...Object.values(allUsers)
1063
+ .filter((user) => users.some((id) => +id === user.id) && !appManagers.some((id) => +id === user.id))
1064
+ .map((user) => ({ name: getUserFullName(user) })),
1065
+ ];
1066
+ prepareUsersConfirmBlock({
1067
+ ...checkUsersResponse,
1068
+ newUserEmails,
1069
+ newProjectManagers,
1070
+ newApplicationAccessUsers,
1071
+ newApplicationManagers,
1221
1072
  });
1222
1073
  }
1223
- promise
1224
- .then(() => {
1225
- if (withCronSync || webhooks) {
1226
- return getSyncSettings('crowdin');
1227
- }
1228
- })
1229
- .catch((e) => catchRejection(e, "Can't fetch Crowdin data"))
1230
- .finally(() => appComponent.current.setAttribute('is-crowdin-loading', false));
1231
- }
1232
- function getIntegrationData({ hardReload = false, parentId = '', search = '', page = 0, recursion = false, paginationData } = {}, recursionState = null) {
1233
- if (!paginationData) {
1234
- appComponent.current.setAttribute('is-integration-loading', true);
1074
+ catch (e) {
1075
+ catchRejection(e, (e === null || e === void 0 ? void 0 : e.error) || "Can't invite users");
1235
1076
  }
1236
- let recursionStateLocal = recursionState;
1237
- return checkOrigin()
1238
- .then((restParams) => {
1239
- let url = `api/integration/data${restParams}&parent_id=${encodeURIComponent(parentId)}&search=${encodeURIComponent(search)}&page=${page}`;
1240
- if (paginationData) {
1241
- url += `&paginationData=${encodeURIComponent(JSON.stringify(paginationData))}`;
1242
- }
1243
- return fetch(url);
1244
- })
1245
- .then(checkResponse)
1246
- .then(async (res) => {
1247
- const files = res.data;
1248
- const stopPagination = res.stopPagination;
1249
- const tree = files.map((e) => {
1250
- const item = {
1251
- parent_id: e.parentId ? e.parentId : '0',
1252
- name: e.name,
1253
- id: e.id,
1254
- customContent: e.customContent,
1255
- labels: e.labels,
1256
- disabled: e.disabled,
1257
- tooltip: e.tooltip,
1258
- pagination: e.pagination,
1259
- };
1260
- if (e.type) {
1261
- item.isNew = e.isNew;
1262
- item.isUpdated = e.isUpdated;
1263
- item.notSynced = e.notSynced;
1264
- item.synced = e.synced;
1265
- item.type = e.type;
1266
- item.node_type = fileType;
1267
- }
1268
- else {
1269
- item.node_type = e.nodeType || folderType;
1270
- }
1271
- return item;
1272
- });
1273
- if ((!paginationData && !parentId && !search) || hardReload) {
1274
- hasLoadMoreElements.current = tree.some((item) => item.node_type === 'load-more');
1275
- }
1276
- const appIntegrationFiles = appComponent.current.querySelector('#integration-files');
1277
- if (hardReload) {
1278
- appComponent.current.setIntegrationFilesData(tree);
1279
- }
1280
- else if (tree.length) {
1281
- appComponent.current.pushIntegrationFilesData(tree).then(() => {
1282
- if (parentId) {
1283
- appIntegrationFiles.getSelected().then(async (selection) => {
1284
- const selectedIds = selection === null || selection === void 0 ? void 0 : selection.filter((node) => node).map(({ id }) => id.toString());
1285
- const filteredNodes = tree.filter((node) => !selectedIds.includes(node.id.toString()) &&
1286
- selectedIds.includes(node.parent_id.toString()));
1287
- if (filteredNodes === null || filteredNodes === void 0 ? void 0 : filteredNodes.length) {
1288
- const filteredNodesId = filteredNodes.map(({ id }) => id.toString());
1289
- await appIntegrationFiles.setSelected(filteredNodesId);
1290
- }
1291
- });
1292
- }
1293
- });
1294
- }
1295
- if (integrationPagination) {
1296
- if (stopPagination) {
1297
- appIntegrationFiles.setAttribute('load-more-disabled', true);
1298
- }
1299
- else {
1300
- appIntegrationFiles.setAttribute('load-more-disabled', false);
1301
- }
1302
- }
1303
- if (search) {
1304
- const openIds = files.filter((e) => !e.type).map((e) => e.id);
1305
- appComponent.current.setIntegrationOpenedFolders(openIds);
1077
+ finally {
1078
+ setTimeout(() => setIsLoading(false), 500);
1079
+ }
1080
+ }
1081
+ function inviteUsers() {
1082
+ setIsLoading(true);
1083
+ const select = usersSelectRef.current;
1084
+ const users = JSON.parse(select.value);
1085
+ const user_ids = users.filter((id) => parseInt(id));
1086
+ const emails = users.filter((id) => !parseInt(id));
1087
+ AP.inviteUsers({ user_ids, emails }, async (res) => {
1088
+ var _a;
1089
+ if (!res.success) {
1090
+ catchRejection(res, ((_a = res === null || res === void 0 ? void 0 : res.error) === null || _a === void 0 ? void 0 : _a.message) || "Can't invite users");
1091
+ setTimeout(() => setIsLoading(false), 500);
1092
+ return;
1306
1093
  }
1307
- if (recursion) {
1308
- if (!recursionStateLocal) {
1309
- recursionStateLocal = {
1310
- openedFolders: new Set(),
1311
- calls: 0,
1312
- setLoading(isLoading) {
1313
- appComponent.current.setAttribute('is-integration-loading', isLoading);
1314
- },
1315
- };
1316
- recursionStateLocal.setLoading(true);
1317
- }
1318
- if (parentId) {
1319
- recursionStateLocal.openedFolders.add(parentId);
1320
- }
1321
- const folderItems = files.filter(isFolder);
1322
- if (folderItems.length === 0) {
1323
- return Promise.resolve();
1094
+ try {
1095
+ const restParams = await checkOrigin();
1096
+ const inviteRes = await checkResponse(await fetch('api/invite-users' + restParams, {
1097
+ method: 'POST',
1098
+ headers: { 'Content-Type': 'application/json' },
1099
+ body: JSON.stringify(res.data),
1100
+ }));
1101
+ if (!inviteRes.success) {
1102
+ catchRejection(inviteRes, (inviteRes === null || inviteRes === void 0 ? void 0 : inviteRes.message) || "Can't invite users");
1103
+ return;
1324
1104
  }
1325
- folderItems.forEach((folder) => recursionStateLocal.openedFolders.add(folder.id));
1326
- const currentOpenedFolders = await appComponent.current.getIntegrationOpenedFolders();
1327
- appComponent.current.setIntegrationOpenedFolders([
1328
- ...currentOpenedFolders,
1329
- ...recursionStateLocal.openedFolders,
1330
- ]);
1331
- const recursivePromises = folderItems.map(async (folder) => {
1332
- recursionStateLocal.calls++;
1333
- try {
1334
- return getIntegrationData({ parentId: folder.id, recursion: true }, recursionState);
1335
- }
1336
- finally {
1337
- recursionStateLocal.calls--;
1338
- if (recursionStateLocal.calls === 0) {
1339
- recursionStateLocal.setLoading(false);
1340
- }
1341
- }
1342
- });
1343
- return Promise.all(recursivePromises);
1344
- }
1345
- })
1346
- .then(() => {
1347
- if (withCronSync || webhooks) {
1348
- getSyncSettings('integration');
1105
+ showToast('Users successfully updated');
1106
+ hideConfirmUsersBlock();
1107
+ closeModal(permissionsModalRef.current);
1349
1108
  }
1350
- })
1351
- .catch((e) => catchRejection(e, `Can't fetch ${name} templates`))
1352
- .finally(() => {
1353
- if (!recursionStateLocal && !recursion) {
1354
- appComponent.current.setAttribute('is-integration-loading', false);
1109
+ catch (e) {
1110
+ catchRejection(e, (e === null || e === void 0 ? void 0 : e.error) || "Can't invite users");
1355
1111
  }
1356
- else if (recursionStateLocal && recursionStateLocal.calls === 0) {
1357
- appComponent.current.setAttribute('is-integration-loading', false);
1112
+ finally {
1113
+ setTimeout(() => setIsLoading(false), 500);
1358
1114
  }
1359
1115
  });
1360
1116
  }
1361
- function isScheduleDisabled(currentConfig = config.current) {
1362
- var _a;
1363
- return String((_a = currentConfig === null || currentConfig === void 0 ? void 0 : currentConfig.schedule) !== null && _a !== void 0 ? _a : '0') === '0';
1364
- }
1365
- async function applyAutoSyncPausedMarkers(provider, syncSettingsFiles = []) {
1366
- var _a, _b, _c, _d;
1367
- const isDisabled = isScheduleDisabled();
1368
- const configuredIds = new Set((Array.isArray(syncSettingsFiles) ? syncSettingsFiles : []).map((item) => `${item.id}`));
1369
- const getChangedItems = (tree = []) => tree
1370
- .map((item) => {
1371
- const nextScheduleDisabled = Boolean(item.schedule) && isDisabled && configuredIds.has(`${item.id}`);
1372
- const currentScheduleDisabled = Boolean(item.scheduleDisabled);
1373
- if (currentScheduleDisabled === nextScheduleDisabled) {
1374
- return null;
1375
- }
1376
- return {
1377
- ...item,
1378
- scheduleDisabled: nextScheduleDisabled,
1379
- };
1380
- })
1381
- .filter(Boolean);
1382
- if (provider === 'crowdin') {
1383
- const changedItems = getChangedItems(crowdinData.current || []);
1384
- if (changedItems.length) {
1385
- const changedById = new Map(changedItems.map((item) => [`${item.id}`, item]));
1386
- crowdinData.current = (crowdinData.current || []).map((item) => changedById.get(`${item.id}`) || item);
1387
- (_a = appComponent.current) === null || _a === void 0 ? void 0 : _a.updateCrowdinFileData(changedItems);
1388
- }
1389
- return;
1390
- }
1391
- const integrationData = await ((_c = (_b = appComponent.current) === null || _b === void 0 ? void 0 : _b.getIntegrationFilesData) === null || _c === void 0 ? void 0 : _c.call(_b));
1392
- if (Array.isArray(integrationData)) {
1393
- const changedItems = getChangedItems(integrationData);
1394
- if (changedItems.length) {
1395
- (_d = appComponent.current) === null || _d === void 0 ? void 0 : _d.updateIntegrationFileData(changedItems);
1396
- }
1397
- }
1398
- }
1399
- async function fetchSyncSettingsFiles(provider) {
1400
- const restParams = await checkOrigin();
1401
- const res = await fetch(`/api/sync-settings/${provider}` + restParams).then(checkResponse);
1402
- return Array.isArray(res) ? res : [];
1403
- }
1404
- async function refreshAutoSyncPausedIndicatorsAndWarning() {
1405
- const [crowdinSyncSettings, integrationSyncSettings] = await Promise.all([
1406
- fetchSyncSettingsFiles('crowdin'),
1407
- fetchSyncSettingsFiles('integration'),
1408
- ]);
1409
- await applyAutoSyncPausedMarkers('crowdin', crowdinSyncSettings);
1410
- await applyAutoSyncPausedMarkers('integration', integrationSyncSettings);
1411
- if (isScheduleDisabled() && (crowdinSyncSettings.length || integrationSyncSettings.length)) {
1412
- showToast('Auto Sync is configured for some files, but sync schedule is disabled in app settings.');
1413
- }
1414
- }
1415
- function getSyncSettings(provider) {
1416
- checkOrigin()
1417
- .then((restParams) => fetch(`/api/sync-settings/${provider}` + restParams))
1418
- .then(checkResponse)
1419
- .then(async (res) => {
1420
- if (provider === 'crowdin') {
1421
- if (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.crowdin) {
1422
- appComponent.current.setCrowdinScheduleSync(res, true, true);
1423
- }
1424
- else {
1425
- appComponent.current.setCrowdinScheduleSync(res);
1426
- }
1427
- }
1428
- else {
1429
- if (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration) {
1430
- appComponent.current.setIntegrationScheduleSync(res, true, true);
1117
+ function prepareUsersConfirmBlock(usersData) {
1118
+ showConfirmUsersBlock();
1119
+ const toNames = (users = []) => users.map((user) => user.name);
1120
+ const applicationSettingsDescription = 'This can be configured in organization settings (Apps section).';
1121
+ const hasApplicationAccessUsers = usersData.newApplicationAccessUsers.length > 0;
1122
+ setPermissionsSummary({
1123
+ ...(hasOrganization
1124
+ ? {
1125
+ organization: {
1126
+ status: usersData.newUserEmails.length
1127
+ ? usersData.inviteRestricted
1128
+ ? 'actionRequired'
1129
+ : 'willBeRegistered'
1130
+ : 'granted',
1131
+ users: toNames(usersData.newUserEmails),
1132
+ description: usersData.inviteRestricted
1133
+ ? 'New user invitations are restricted to administrators.'
1134
+ : '',
1135
+ isWarning: usersData.inviteRestricted,
1136
+ },
1431
1137
  }
1432
- else {
1433
- appComponent.current.setIntegrationScheduleSync(res);
1434
- }
1435
- }
1436
- await applyAutoSyncPausedMarkers(provider, res);
1437
- })
1438
- .catch((e) => catchRejection(e, "Can't fetch file progress"));
1439
- }
1440
- function getFileProgress(fileId) {
1441
- checkOrigin()
1442
- .then((restParams) => fetch(`api/crowdin/file-progress/${fileId}` + restParams))
1443
- .then(checkResponse)
1444
- .then((res) => appComponent.current.addCrowdinFileProgress(res))
1445
- .catch((e) => catchRejection(e, "Can't fetch file progress"));
1138
+ : {}),
1139
+ project: {
1140
+ status: usersData.newProjectManagers.length ? 'willBeGranted' : 'granted',
1141
+ users: toNames(usersData.newProjectManagers),
1142
+ },
1143
+ applicationSettings: {
1144
+ status: hasApplicationAccessUsers
1145
+ ? usersData.isAdmin
1146
+ ? 'willBeGranted'
1147
+ : 'actionRequired'
1148
+ : 'granted',
1149
+ users: toNames(usersData.newApplicationAccessUsers),
1150
+ description: hasApplicationAccessUsers
1151
+ ? usersData.isAdmin
1152
+ ? applicationSettingsDescription
1153
+ : `${applicationSettingsDescription} Only organization admins have permission to update this setting.`
1154
+ : applicationSettingsDescription,
1155
+ isWarning: hasApplicationAccessUsers && !usersData.isAdmin,
1156
+ },
1157
+ applicationCredentials: {
1158
+ status: usersData.newApplicationManagers.length ? 'willBeGranted' : 'granted',
1159
+ users: toNames(usersData.newApplicationManagers),
1160
+ },
1161
+ });
1446
1162
  }
1447
- function uploadFilesToCrowdin(event, languages = []) {
1448
- var _a, _b, _c, _d, _e;
1449
- let files = [];
1450
- let uploadTranslations = false;
1451
- let forcePushSources = false;
1452
- if (((_a = event.detail) === null || _a === void 0 ? void 0 : _a.action) === 'uploadTranslations') {
1453
- files = event.detail.files;
1454
- uploadTranslations = true;
1455
- forcePushSources = true;
1456
- }
1457
- else if (((_b = event.detail) === null || _b === void 0 ? void 0 : _b.action) === 'excludedTargetLanguages') {
1458
- fileToSync.current = event.detail.files;
1459
- openLanguageModal();
1163
+ return {
1164
+ usersSelectRef,
1165
+ isLoading,
1166
+ permissionUserOptions,
1167
+ permissionsSelectedValue,
1168
+ permissionsSummary,
1169
+ isPermissionsConfirmVisible,
1170
+ showPermissionsDialog,
1171
+ inviteUsers,
1172
+ };
1173
+ }
1174
+
1175
+ /// <reference path="../../../../ui/types.d.ts" />
1176
+ function useSchedule({ appComponent, config, scheduleModalRef, withCronSync, webhooks, syncNewElements, integrationOneLevelFetching, hasLoadMoreElements, updateSyncSettings, }) {
1177
+ const selectedFilesCheckboxRef = reactExports.useRef(null);
1178
+ const allFolderFilesCheckboxRef = reactExports.useRef(null);
1179
+ const newFilesCheckboxRef = reactExports.useRef(null);
1180
+ const syncData = reactExports.useRef(null);
1181
+ const [scheduleModalState, setScheduleModalState] = reactExports.useState(getDefaultScheduleModalState);
1182
+ const [isScheduleSaveDisabled, setIsScheduleSaveDisabled] = reactExports.useState(false);
1183
+ reactExports.useEffect(() => {
1184
+ syncScheduleCheckbox(selectedFilesCheckboxRef.current, {
1185
+ checked: scheduleModalState.selectedFiles,
1186
+ });
1187
+ syncScheduleCheckbox(allFolderFilesCheckboxRef.current, {
1188
+ checked: scheduleModalState.allFolderFiles,
1189
+ hidden: !scheduleModalState.showAllFolderFiles,
1190
+ });
1191
+ syncScheduleCheckbox(newFilesCheckboxRef.current, {
1192
+ checked: scheduleModalState.newFiles,
1193
+ hidden: !scheduleModalState.showNewFiles,
1194
+ clearSelection: scheduleModalState.newFilesClearSelection,
1195
+ });
1196
+ }, [scheduleModalState]);
1197
+ reactExports.useEffect(() => {
1198
+ if (!withCronSync && !webhooks) {
1460
1199
  return;
1461
1200
  }
1462
- else if (((_c = event.detail) === null || _c === void 0 ? void 0 : _c.action) === 'forcePushSources') {
1463
- files = event.detail.files;
1464
- forcePushSources = true;
1201
+ const listeners = {
1202
+ integrationScheduleSync: setIntegrationScheduleSync,
1203
+ crowdinScheduleSync: setCrowdinScheduleSync,
1204
+ crowdinDisableSync: disableCrowdinSync,
1205
+ integrationDisableSync: disableIntegrationSync,
1206
+ };
1207
+ for (const [event, handler] of Object.entries(listeners)) {
1208
+ document.body.addEventListener(event, handler);
1465
1209
  }
1466
- else if (((_d = event.detail) === null || _d === void 0 ? void 0 : _d.action) === 'syncTranslationsToIntegration') {
1467
- syncTranslationsToIntegration(event.detail.files);
1210
+ return () => {
1211
+ for (const [event, handler] of Object.entries(listeners)) {
1212
+ document.body.removeEventListener(event, handler);
1213
+ }
1214
+ };
1215
+ }, []);
1216
+ function syncScheduleCheckbox(checkbox, { checked, hidden = false, clearSelection = false, }) {
1217
+ if (!checkbox) {
1468
1218
  return;
1469
1219
  }
1220
+ checkbox.checked = checked;
1221
+ checkbox.value = checked;
1222
+ if (hidden) {
1223
+ checkbox.setAttribute('hidden', '');
1224
+ }
1470
1225
  else {
1471
- files = event.detail;
1226
+ checkbox.removeAttribute('hidden');
1472
1227
  }
1473
- if (languages.length > 0) {
1474
- forcePushSources = true;
1228
+ if (clearSelection) {
1229
+ checkbox.setAttribute('clear-selection', '');
1475
1230
  }
1476
- if (((_e = event.detail) === null || _e === void 0 ? void 0 : _e.length) === 0 || !event.detail) {
1477
- showToast('Select templates which will be pushed to Crowdin');
1478
- return;
1231
+ else {
1232
+ checkbox.removeAttribute('clear-selection');
1479
1233
  }
1480
- appComponent.current.setAttribute('is-to-crowdin-process', true);
1481
- let req;
1482
- if (integrationOneLevelFetching) {
1483
- req = files.map((f) => ({
1484
- name: f.name,
1485
- id: f.id,
1486
- type: f.type,
1487
- parentId: f.parent_id,
1488
- nodeType: f.node_type,
1489
- isUpdated: f.isUpdated,
1490
- }));
1234
+ }
1235
+ async function hasFolder(selectedFiles) {
1236
+ let found;
1237
+ if (Array.isArray(selectedFiles)) {
1238
+ found = selectedFiles.find((file) => file.node_type === FOLDER_TYPE || file.node_type === BRANCH_TYPE);
1491
1239
  }
1492
1240
  else {
1493
- req = files
1494
- .filter((f) => f.node_type !== folderType)
1495
- .map((f) => ({
1496
- name: f.name,
1497
- id: f.id,
1498
- type: f.type,
1499
- parentId: f.parent_id,
1500
- isUpdated: f.isUpdated,
1501
- }));
1241
+ const files = await appComponent.current.getCrowdinFilesData();
1242
+ const folders = files.filter((file) => file.node_type === FOLDER_TYPE || file.node_type === BRANCH_TYPE);
1243
+ found = folders.find((folder) => selectedFiles.hasOwnProperty(folder.id));
1502
1244
  }
1503
- checkOrigin()
1504
- .then((restParams) => fetch(`api/crowdin/update${restParams}&uploadTranslations=${uploadTranslations}&languages=${languages}&forcePushSources=${forcePushSources}`, {
1505
- method: 'POST',
1506
- headers: { 'Content-Type': 'application/json' },
1507
- body: JSON.stringify(req),
1508
- }))
1509
- .then(checkResponse)
1510
- .then((res) => {
1511
- checkJob({
1512
- jobId: res === null || res === void 0 ? void 0 : res.jobId,
1513
- jobType: JOB_TYPE.updateCrowdin,
1514
- });
1515
- })
1516
- .catch((e) => catchRejection(e, "Can't upload templates to Crowdin"));
1517
- }
1518
- function getActiveJobs() {
1519
- checkOrigin()
1520
- .then((restParams) => fetch(`api/jobs${restParams}`))
1521
- .then(checkResponse)
1522
- .then((jobs) => {
1523
- if (!Array.isArray(jobs)) {
1524
- return;
1525
- }
1526
- jobs.forEach((job) => {
1527
- switch (job.type) {
1528
- case JOB_TYPE.updateCrowdin:
1529
- case JOB_TYPE.updateTargetLanguages:
1530
- appComponent.current.setAttribute('is-to-crowdin-process', true);
1531
- break;
1532
- case JOB_TYPE.updateIntegration:
1533
- appComponent.current.setAttribute('is-to-integration-process', true);
1534
- break;
1535
- case JOB_TYPE.crowdinSyncSettingsSave:
1536
- appComponent.current.setAttribute('is-crowdin-sync-settings-in-progress', true);
1537
- break;
1538
- case JOB_TYPE.integrationSyncSettingsSave:
1539
- appComponent.current.setAttribute('is-integration-sync-settings-in-progress', true);
1540
- break;
1541
- }
1542
- checkJob({
1543
- jobId: job.id,
1544
- jobType: job.type,
1545
- });
1546
- });
1547
- })
1548
- .catch((e) => catchRejection(e, 'Sync status check failed'));
1245
+ return found !== undefined;
1549
1246
  }
1550
- function cancelJob(jobId) {
1551
- var _a;
1552
- if ((_a = asyncJobs.current[jobId]) === null || _a === void 0 ? void 0 : _a.isFailed) {
1553
- return;
1554
- }
1555
- checkOrigin()
1556
- .then((restParams) => fetch('api/jobs' + restParams + '&jobId=' + jobId, {
1557
- method: 'DELETE',
1558
- headers: { 'Content-Type': 'application/json' },
1559
- }))
1560
- .then(checkResponse)
1561
- .then(() => showToast('Cancellation…'))
1562
- .catch((e) => catchRejection(e, 'Sync cancellation failed'));
1247
+ function getIntegrationFoldersToExpand(allData, syncedFiles) {
1248
+ return Array.isArray(allData)
1249
+ ? allData
1250
+ .filter((node) => node.node_type === FOLDER_TYPE &&
1251
+ !syncedFiles.find((syncedFile) => syncedFile.parent_id === node.id))
1252
+ .map((node) => ({ ...node, nodeType: node.node_type, parentId: node.parent_id }))
1253
+ : [];
1563
1254
  }
1564
- function checkJob({ jobId, jobType, onSuccess, onError, onFinally }) {
1565
- switch (jobType) {
1566
- case JOB_TYPE.updateCrowdin:
1567
- case JOB_TYPE.updateTargetLanguages:
1568
- if (!onFinally) {
1569
- onFinally = () => {
1570
- appComponent.current.setAttribute('is-to-crowdin-process', false);
1571
- getCrowdinData();
1572
- };
1573
- }
1574
- break;
1575
- case JOB_TYPE.updateIntegration:
1576
- if (!onFinally) {
1577
- onFinally = () => appComponent.current.setAttribute('is-to-integration-process', false);
1578
- }
1579
- break;
1580
- case JOB_TYPE.crowdinSyncSettingsSave:
1581
- if (!onFinally) {
1582
- onFinally = () => appComponent.current.setAttribute('is-crowdin-sync-settings-in-progress', false);
1583
- }
1584
- break;
1585
- case JOB_TYPE.integrationSyncSettingsSave:
1586
- if (!onFinally) {
1587
- onFinally = () => appComponent.current.setAttribute('is-integration-sync-settings-in-progress', false);
1588
- }
1589
- break;
1590
- }
1591
- checkOrigin()
1592
- .then((restParams) => fetch('api/jobs' + restParams + '&jobId=' + jobId))
1593
- .then(checkResponse)
1594
- .then((job) => {
1595
- const isFailed = [JOB_STATUS.failed, JOB_STATUS.canceled].includes(job.status);
1596
- const progress = isFailed || JOB_STATUS.finished === job.status ? 100 : job.progress;
1597
- const info = JOB_STATUS.canceled === job.status ? `Cancelled\n${job.info || ''}` : job.info;
1598
- if (!silentJobs.find((type) => type === jobType)) {
1599
- pushJobs([
1600
- {
1601
- id: job.id,
1602
- title: job.title,
1603
- progress,
1604
- info,
1605
- isFailed,
1606
- },
1607
- ]);
1608
- }
1609
- if (isFailed) {
1610
- if (onError) {
1611
- onError();
1612
- }
1255
+ async function openScheduleModal(type, options = {}) {
1256
+ var _a, _b, _c, _d;
1257
+ const { showAllFolderFiles = false, showNewFiles = true } = options;
1258
+ let newFileStatus = 'unChecked';
1259
+ const integrationSyncFolders = ((await ((_b = (_a = appComponent.current) === null || _a === void 0 ? void 0 : _a.getIntegrationScheduleSync) === null || _b === void 0 ? void 0 : _b.call(_a, true))) || []).filter((elements) => elements.node_type === FOLDER_TYPE);
1260
+ const integrationFiles = (_c = appComponent.current) === null || _c === void 0 ? void 0 : _c.querySelector('#integration-files');
1261
+ const selectedIntegrationFolders = ((await ((_d = integrationFiles === null || integrationFiles === void 0 ? void 0 : integrationFiles.getSelected) === null || _d === void 0 ? void 0 : _d.call(integrationFiles, true))) || []).filter((elements) => elements.node_type === FOLDER_TYPE);
1262
+ for (let i = 0; i < selectedIntegrationFolders.length; i++) {
1263
+ const selectedElement = selectedIntegrationFolders[i];
1264
+ const found = integrationSyncFolders.some((integrationElement) => integrationElement.id === selectedElement.id);
1265
+ if (found) {
1266
+ newFileStatus = 'checked';
1613
1267
  }
1614
- else if ([JOB_STATUS.created, JOB_STATUS.inProgress].includes(job.status) && job.progress < 100) {
1615
- setTimeout(() => {
1616
- checkJob({ jobId, jobType, onSuccess, onError, onFinally });
1617
- }, asyncProgress.checkInterval);
1618
- return;
1268
+ else if (!found && newFileStatus === 'checked') {
1269
+ newFileStatus = 'clear-selection';
1270
+ break;
1619
1271
  }
1620
1272
  else {
1621
- if (onSuccess) {
1622
- onSuccess(job);
1623
- }
1624
- }
1625
- if (onFinally) {
1626
- onFinally(job);
1627
- }
1628
- })
1629
- .catch((e) => {
1630
- if (onFinally) {
1631
- onFinally();
1632
- }
1633
- if (!silentJobs.find((type) => type === jobType)) {
1634
- pushJobs([
1635
- {
1636
- id: jobId,
1637
- isFailed: true,
1638
- },
1639
- ]);
1273
+ newFileStatus = 'unChecked';
1640
1274
  }
1641
- showToast('Sync status check failed');
1642
- });
1643
- }
1644
- function pushJobs(jobs) {
1645
- const el = asyncProgressRef.current;
1646
- if (el && el.pushJobs) {
1647
- el.pushJobs(jobs);
1648
- jobs.forEach((job) => (asyncJobs.current[job.id] = job));
1649
- }
1650
- else {
1651
- setTimeout(() => {
1652
- pushJobs(jobs);
1653
- }, 300);
1654
1275
  }
1276
+ setScheduleModalState({
1277
+ type,
1278
+ showAllFolderFiles,
1279
+ showNewFiles,
1280
+ newFiles: showNewFiles && newFileStatus === 'checked',
1281
+ newFilesClearSelection: showNewFiles && newFileStatus === 'clear-selection',
1282
+ selectedFiles: true,
1283
+ allFolderFiles: false,
1284
+ });
1285
+ setIsScheduleSaveDisabled(false);
1286
+ openModal(scheduleModalRef.current);
1655
1287
  }
1656
- function uploadFilesToIntegration(event) {
1657
- var _a, _b, _c, _d;
1658
- let files = {};
1659
- let forcePushTranslations = false;
1660
- if (((_a = event.detail) === null || _a === void 0 ? void 0 : _a.action) === 'forcePushTranslations') {
1661
- files = event.detail.files;
1662
- forcePushTranslations = true;
1663
- }
1664
- else if (((_b = event.detail) === null || _b === void 0 ? void 0 : _b.action) === 'excludedTargetLanguages') {
1665
- crowdinFilesToUpdateTargetLanguages.current = event.detail.files;
1666
- openLanguageModalForCrowdinFiles();
1667
- return;
1288
+ function onChangeAutoSynchronizationOptions() {
1289
+ var _a, _b, _c;
1290
+ const nextState = {
1291
+ newFiles: scheduleModalState.showNewFiles ? !!((_a = newFilesCheckboxRef.current) === null || _a === void 0 ? void 0 : _a.checked) : false,
1292
+ selectedFiles: !!((_b = selectedFilesCheckboxRef.current) === null || _b === void 0 ? void 0 : _b.checked),
1293
+ allFolderFiles: scheduleModalState.showAllFolderFiles
1294
+ ? !!((_c = allFolderFilesCheckboxRef.current) === null || _c === void 0 ? void 0 : _c.checked)
1295
+ : false,
1296
+ };
1297
+ setScheduleModalState((currentState) => ({
1298
+ ...currentState,
1299
+ ...nextState,
1300
+ newFilesClearSelection: false,
1301
+ }));
1302
+ setIsScheduleSaveDisabled(!nextState.newFiles && !nextState.selectedFiles && !nextState.allFolderFiles);
1303
+ }
1304
+ async function saveScheduleSync() {
1305
+ const { type, newFiles: newFile, selectedFiles, allFolderFiles } = scheduleModalState;
1306
+ if (type === 'crowdin') {
1307
+ const crowdinFiles = await appComponent.current.getCrowdinFilesData();
1308
+ const syncedFileIds = Object.keys(syncData.current);
1309
+ const crowdinFolderIds = crowdinFiles
1310
+ .filter((file) => file.node_type === FOLDER_TYPE || file.node_type === BRANCH_TYPE)
1311
+ .filter((file) => syncedFileIds.includes(String(file.id)))
1312
+ .map((file) => String(file.id));
1313
+ appComponent.current.setCrowdinScheduleSync(syncData.current, newFile, selectedFiles);
1314
+ appComponent.current.setCrowdinFilesHasSyncOption(true);
1315
+ const syncedFiles = await appComponent.current.getCrowdinScheduleSync(true);
1316
+ appComponent.current.setAttribute('is-crowdin-sync-settings-in-progress', true);
1317
+ updateSyncSettings(syncedFiles, 'schedule', 'crowdin', [], crowdinFolderIds);
1668
1318
  }
1669
- else if (((_c = event.detail) === null || _c === void 0 ? void 0 : _c.action) === 'syncSourcesToCrowdin') {
1670
- syncToCrowdinFromCrowdinFiles(event.detail.files, false);
1671
- return;
1319
+ else if (type === 'integration') {
1320
+ if (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration) {
1321
+ appComponent.current.setIntegrationScheduleSync(syncData.current, newFile, selectedFiles);
1322
+ }
1323
+ else {
1324
+ appComponent.current.setIntegrationScheduleSync(syncData.current);
1325
+ }
1326
+ appComponent.current.setIntegrationFilesHasSyncOption(true);
1327
+ const syncedFiles = await appComponent.current.getIntegrationScheduleSync(true);
1328
+ appComponent.current.setAttribute('is-integration-sync-settings-in-progress', true);
1329
+ if (integrationOneLevelFetching) {
1330
+ updateSyncSettings(syncedFiles, 'schedule', 'integration', selectedFiles ? getIntegrationFoldersToExpand(syncData.current, syncedFiles) : []);
1331
+ }
1332
+ else {
1333
+ if (allFolderFiles) {
1334
+ const integrationExpandedFolders = syncData.current
1335
+ .filter((file) => file.node_type === FOLDER_TYPE)
1336
+ .map((file) => ({ ...file, nodeType: file.node_type, parentId: file.parent_id }));
1337
+ updateSyncSettings(syncedFiles, 'schedule', 'integration', integrationExpandedFolders);
1338
+ }
1339
+ else {
1340
+ updateSyncSettings(syncedFiles, 'schedule', 'integration');
1341
+ }
1342
+ }
1672
1343
  }
1673
- else if (((_d = event.detail) === null || _d === void 0 ? void 0 : _d.action) === 'syncTranslationsToCrowdin') {
1674
- syncToCrowdinFromCrowdinFiles(event.detail.files, true);
1344
+ closeModal(scheduleModalRef.current);
1345
+ }
1346
+ async function setIntegrationScheduleSync(e) {
1347
+ var _a;
1348
+ if (e.detail.length === 0) {
1349
+ showToast('Select templates which will be pushed to Crowdin');
1675
1350
  return;
1676
1351
  }
1352
+ let syncedFiles;
1353
+ if (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration) {
1354
+ const newIntegrationFiles = (_a = config.current) === null || _a === void 0 ? void 0 : _a['new-integration-files'];
1355
+ syncData.current = e.detail;
1356
+ const isFolder = await hasFolder(e.detail);
1357
+ const shouldShowModal = isFolder && !newIntegrationFiles;
1358
+ const showNewFiles = true;
1359
+ const showAllFolderFiles = hasLoadMoreElements.current;
1360
+ if (shouldShowModal) {
1361
+ await openScheduleModal('integration', { showNewFiles, showAllFolderFiles });
1362
+ return;
1363
+ }
1364
+ appComponent.current.setIntegrationScheduleSync(e.detail);
1365
+ appComponent.current.setIntegrationFilesHasSyncOption(true);
1366
+ syncedFiles = await appComponent.current.getIntegrationScheduleSync(true);
1367
+ }
1677
1368
  else {
1678
- files = event.detail;
1369
+ syncData.current = e.detail;
1370
+ const isFolder = await hasFolder(e.detail);
1371
+ if (hasLoadMoreElements.current && isFolder) {
1372
+ await openScheduleModal('integration', { showNewFiles: false, showAllFolderFiles: true });
1373
+ return;
1374
+ }
1375
+ syncedFiles = await appComponent.current.getIntegrationScheduleSync();
1679
1376
  }
1680
- if (Object.keys(event.detail).length === 0 || !event.detail) {
1681
- showToast('Select files which will be uploaded to ${name}');
1682
- return;
1377
+ appComponent.current.setAttribute('is-integration-sync-settings-in-progress', true);
1378
+ if (integrationOneLevelFetching) {
1379
+ updateSyncSettings(syncedFiles, 'schedule', 'integration', getIntegrationFoldersToExpand(e.detail, syncedFiles));
1380
+ }
1381
+ else {
1382
+ if (hasLoadMoreElements.current) {
1383
+ const integrationExpandedFolders = e.detail
1384
+ .filter((file) => file.node_type === FOLDER_TYPE)
1385
+ .map((file) => ({ ...file, nodeType: file.node_type, parentId: file.parent_id }));
1386
+ updateSyncSettings(syncedFiles, 'schedule', 'integration', integrationExpandedFolders);
1387
+ }
1388
+ else {
1389
+ updateSyncSettings(syncedFiles, 'schedule', 'integration');
1390
+ }
1683
1391
  }
1684
- appComponent.current.setAttribute('is-to-integration-process', true);
1685
- const req = {};
1686
- Object.keys(files)
1687
- .filter((id) => crowdinData.current.find((c) => c.id === id).node_type === fileType)
1688
- .forEach((id) => (req[id] = files[id]));
1689
- checkOrigin()
1690
- .then((restParams) => fetch(`api/integration/update${restParams}&forcePushTranslations=${forcePushTranslations}`, {
1691
- method: 'POST',
1692
- headers: { 'Content-Type': 'application/json' },
1693
- body: JSON.stringify(req),
1694
- }))
1695
- .then(checkResponse)
1696
- .then((res) => {
1697
- checkJob({
1698
- jobId: res === null || res === void 0 ? void 0 : res.jobId,
1699
- jobType: JOB_TYPE.updateIntegration,
1700
- });
1701
- })
1702
- .catch((e) => catchRejection(e, `Can't upload files to ${name}`));
1703
1392
  }
1704
- function syncToCrowdinFromCrowdinFiles(files, uploadTranslations) {
1705
- if (!files || Object.keys(files).length === 0) {
1706
- showToast('Select files to sync');
1393
+ async function setCrowdinScheduleSync(e) {
1394
+ var _a;
1395
+ if (e.detail.length === 0) {
1396
+ showToast('Select templates which will be pushed to Crowdin');
1707
1397
  return;
1708
1398
  }
1709
- appComponent.current.setAttribute('is-to-crowdin-process', true);
1710
- const req = {};
1711
- Object.keys(files)
1712
- .filter((id) => { var _a; return ((_a = crowdinData.current.find((c) => c.id === id)) === null || _a === void 0 ? void 0 : _a.node_type) === fileType; })
1713
- .forEach((id) => (req[id] = files[id]));
1714
- checkOrigin()
1715
- .then((restParams) => fetch(`api/crowdin/update${restParams}&matchFromCrowdin=true&uploadTranslations=${uploadTranslations}`, {
1716
- method: 'POST',
1717
- headers: { 'Content-Type': 'application/json' },
1718
- body: JSON.stringify(req),
1719
- }))
1720
- .then(checkResponse)
1721
- .then((res) => {
1722
- checkJob({
1723
- jobId: res === null || res === void 0 ? void 0 : res.jobId,
1724
- jobType: JOB_TYPE.updateCrowdin,
1725
- });
1726
- })
1727
- .catch((e) => catchRejection(e, "Can't sync files to Crowdin"));
1399
+ let syncedFiles;
1400
+ if (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.crowdin) {
1401
+ const newCrowdinFiles = (_a = config.current) === null || _a === void 0 ? void 0 : _a['new-crowdin-files'];
1402
+ syncData.current = e.detail;
1403
+ const isFolder = await hasFolder(e.detail);
1404
+ if (isFolder && !newCrowdinFiles) {
1405
+ await openScheduleModal('crowdin');
1406
+ return;
1407
+ }
1408
+ appComponent.current.setCrowdinScheduleSync(e.detail);
1409
+ appComponent.current.setCrowdinFilesHasSyncOption(true);
1410
+ syncedFiles = await appComponent.current.getCrowdinScheduleSync(true);
1411
+ }
1412
+ else {
1413
+ syncedFiles = await appComponent.current.getCrowdinScheduleSync();
1414
+ }
1415
+ appComponent.current.setAttribute('is-crowdin-sync-settings-in-progress', true);
1416
+ updateSyncSettings(syncedFiles, 'schedule', 'crowdin');
1728
1417
  }
1729
- function syncTranslationsToIntegration(files) {
1730
- if (!files || files.length === 0) {
1731
- showToast('Select files to sync');
1418
+ async function disableIntegrationSync(e) {
1419
+ if (e.detail.length === 0) {
1420
+ showToast('Select templates which will be pushed to Crowdin');
1732
1421
  return;
1733
1422
  }
1734
- appComponent.current.setAttribute('is-to-integration-process', true);
1735
- const req = files
1736
- .filter((f) => f.node_type !== folderType)
1737
- .map((f) => ({
1738
- name: f.name,
1739
- id: f.id,
1740
- type: f.type,
1741
- parentId: f.parent_id,
1742
- }));
1743
- checkOrigin()
1744
- .then((restParams) => fetch(`api/integration/update${restParams}&matchFromIntegration=true`, {
1745
- method: 'POST',
1746
- headers: { 'Content-Type': 'application/json' },
1747
- body: JSON.stringify(req),
1748
- }))
1749
- .then(checkResponse)
1750
- .then((res) => {
1751
- checkJob({
1752
- jobId: res === null || res === void 0 ? void 0 : res.jobId,
1753
- jobType: JOB_TYPE.updateIntegration,
1754
- });
1755
- })
1756
- .catch((e) => catchRejection(e, `Can't sync translations to ${name}`));
1757
- }
1758
- function openLanguageModal() {
1759
- setLanguageModalState((state) => {
1760
- var _a, _b;
1761
- return ({
1762
- languages: (_b = (_a = state === null || state === void 0 ? void 0 : state.languages) !== null && _a !== void 0 ? _a : project.current.targetLanguages) !== null && _b !== void 0 ? _b : [],
1763
- action: 'syncToCrowdin',
1764
- });
1765
- });
1766
- openModal(languageModal.current);
1767
- }
1768
- function openLanguageModalForCrowdinFiles() {
1769
- setLanguageModalState((state) => {
1770
- var _a, _b;
1771
- return ({
1772
- languages: (_b = (_a = state === null || state === void 0 ? void 0 : state.languages) !== null && _a !== void 0 ? _a : project.current.targetLanguages) !== null && _b !== void 0 ? _b : [],
1773
- action: 'updateCrowdinTargetLanguages',
1774
- });
1775
- });
1776
- openModal(languageModal.current);
1777
- }
1778
- async function uploadFilesToCrowdinWithExcludedLanguages() {
1779
- const selectedLanguages = await languageSelectRef.current.getValue();
1780
- closeModal(languageModal.current);
1781
- uploadFilesToCrowdin({ detail: fileToSync.current }, selectedLanguages);
1423
+ appComponent.current.setAttribute('is-integration-sync-settings-in-progress', true);
1424
+ const syncedFiles = (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration)
1425
+ ? await appComponent.current.getIntegrationScheduleSync(true)
1426
+ : await appComponent.current.getIntegrationScheduleSync();
1427
+ updateSyncSettings(syncedFiles, 'schedule', 'integration');
1782
1428
  }
1783
- async function updateCrowdinFilesTargetLanguages() {
1784
- const selectedLanguages = await languageSelectRef.current.getValue();
1785
- closeModal(languageModal.current);
1786
- const fileIds = Object.keys(crowdinFilesToUpdateTargetLanguages.current).map((id) => parseInt(id, 10));
1787
- if (fileIds.length === 0) {
1788
- showToast('Select files to update');
1429
+ async function disableCrowdinSync(e) {
1430
+ if (e.detail.length === 0) {
1431
+ showToast('Select templates which will be pushed to Crowdin');
1789
1432
  return;
1790
1433
  }
1791
- appComponent.current.setAttribute('is-to-crowdin-process', true);
1792
- checkOrigin()
1793
- .then((restParams) => fetch(`api/crowdin/files-target-languages${restParams}`, {
1794
- method: 'POST',
1795
- headers: { 'Content-Type': 'application/json' },
1796
- body: JSON.stringify({ fileIds, languages: selectedLanguages }),
1797
- }))
1798
- .then(checkResponse)
1799
- .then((res) => {
1800
- checkJob({
1801
- jobId: res === null || res === void 0 ? void 0 : res.jobId,
1802
- jobType: JOB_TYPE.updateTargetLanguages,
1803
- });
1804
- })
1805
- .catch((e) => catchRejection(e, "Can't update files target languages"));
1806
- }
1807
- function setLanguagesForTranslation(languages, action = 'syncToCrowdin') {
1808
- setLanguageModalState({ languages, action });
1434
+ appComponent.current.setAttribute('is-crowdin-sync-settings-in-progress', true);
1435
+ const syncedFiles = (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.crowdin)
1436
+ ? await appComponent.current.getCrowdinScheduleSync(true)
1437
+ : await appComponent.current.getCrowdinScheduleSync();
1438
+ updateSyncSettings(syncedFiles, 'schedule', 'crowdin');
1809
1439
  }
1810
- function hideConfirmUsersBlock() {
1811
- var _a;
1812
- setIsPermissionsConfirmVisible(false);
1813
- (_a = permissionsModalRef.current) === null || _a === void 0 ? void 0 : _a.setAttribute('body-overflow-unset', true);
1440
+ return {
1441
+ selectedFilesCheckboxRef,
1442
+ allFolderFilesCheckboxRef,
1443
+ newFilesCheckboxRef,
1444
+ scheduleModalState,
1445
+ isScheduleSaveDisabled,
1446
+ onChangeAutoSynchronizationOptions,
1447
+ saveScheduleSync,
1448
+ };
1449
+ }
1450
+
1451
+ /// <reference path="../../../../ui/types.d.ts" />
1452
+ function useSettings({ configurationFields, config, settingsModalRef, reloadOnConfigSave, refreshDependencies, checkJob, getCrowdinData, getIntegrationData, refreshAutoSyncPausedIndicatorsAndWarning, }) {
1453
+ const settingsFieldRefs = reactExports.useRef({});
1454
+ const [isLoading, setIsLoading] = reactExports.useState(false);
1455
+ const [isSettingsSaveDisabled, setIsSettingsSaveDisabled] = reactExports.useState(false);
1456
+ function triggerEvent(el, type) {
1457
+ const e = document.createEvent('HTMLEvents');
1458
+ e.initEvent(type, false, true);
1459
+ el.dispatchEvent(e);
1814
1460
  }
1815
- function getUserFullName(user) {
1816
- if (user.full_name) {
1817
- return user.full_name;
1461
+ function parseCrowdinSelectValue(value) {
1462
+ if (Array.isArray(value)) {
1463
+ return value;
1464
+ }
1465
+ if (value === undefined || value === null || value === '') {
1466
+ return [];
1467
+ }
1468
+ try {
1469
+ const parsedValue = JSON.parse(value);
1470
+ return Array.isArray(parsedValue) ? parsedValue : [parsedValue];
1471
+ }
1472
+ catch {
1473
+ return [value];
1818
1474
  }
1819
- const ownerFullName = user.name || ((user.first_name || '') + ' ' + (user.last_name || '')).trim();
1820
- return !!ownerFullName && (user === null || user === void 0 ? void 0 : user.login) !== ownerFullName ? `${ownerFullName} (${user === null || user === void 0 ? void 0 : user.login})` : user === null || user === void 0 ? void 0 : user.login;
1821
1475
  }
1822
- function showPermissionsDialog() {
1823
- hideConfirmUsersBlock();
1824
- openModal(permissionsModalRef.current);
1825
- setLoader('#permissions-modal');
1826
- setPermissionsSelectedValue('[]');
1827
- Promise.all([
1828
- new Promise((resolve) => {
1829
- checkOrigin()
1830
- .then((restParams) => fetch('api/users' + restParams))
1831
- .then(checkResponse)
1832
- .then((res) => {
1833
- resolve(JSON.stringify(res.data.managers));
1834
- });
1835
- }),
1836
- new Promise((resolve) => {
1837
- AP.getUsers((res) => {
1838
- const users = Object.values(res).filter((user) => user.id !== Number(userId));
1839
- resolve(users);
1840
- });
1841
- }),
1842
- ])
1843
- .then(([managers, users]) => {
1844
- const parsedManagers = (JSON.parse(managers) || []);
1845
- const availableUsers = users;
1846
- const validUserIds = new Set(availableUsers.map((user) => `${user.id}`));
1847
- const filteredManagers = parsedManagers.filter((id) => validUserIds.has(id));
1848
- setPermissionUserOptions(availableUsers.map((user) => ({
1849
- id: String(user.id),
1850
- label: getUserFullName(user),
1851
- })));
1852
- setPermissionsSelectedValue(JSON.stringify(filteredManagers));
1853
- })
1854
- .catch((e) => catchRejection(e, "Can't fetch users"))
1855
- .finally(() => unsetLoader('#permissions-modal'));
1476
+ function registerSettingsField(key) {
1477
+ return (element) => {
1478
+ if (!key) {
1479
+ return;
1480
+ }
1481
+ if (element) {
1482
+ settingsFieldRefs.current[key] = element;
1483
+ }
1484
+ else {
1485
+ delete settingsFieldRefs.current[key];
1486
+ }
1487
+ };
1856
1488
  }
1857
- function showConfirmUsersBlock() {
1858
- var _a;
1859
- setIsPermissionsConfirmVisible(true);
1860
- (_a = permissionsModalRef.current) === null || _a === void 0 ? void 0 : _a.removeAttribute('body-overflow-unset');
1489
+ function settingsFieldChange(eventOrElement) {
1490
+ var _a, _b;
1491
+ const el = (eventOrElement === null || eventOrElement === void 0 ? void 0 : eventOrElement.currentTarget) || (eventOrElement === null || eventOrElement === void 0 ? void 0 : eventOrElement.target) || eventOrElement;
1492
+ (_a = el === null || el === void 0 ? void 0 : el.removeAttribute) === null || _a === void 0 ? void 0 : _a.call(el, 'error');
1493
+ (_b = el === null || el === void 0 ? void 0 : el.removeAttribute) === null || _b === void 0 ? void 0 : _b.call(el, 'error-text');
1861
1494
  }
1862
- function checkUsers() {
1863
- setLoader('#permissions-modal');
1864
- const select = usersSelectRef.current;
1865
- if (select.value === '[]') {
1866
- hideConfirmUsersBlock();
1867
- unsetLoader('#permissions-modal');
1868
- return;
1869
- }
1870
- const users = JSON.parse(select.value);
1871
- const user_ids = users.filter((id) => parseInt(id));
1872
- const emails = users.filter((id) => !parseInt(id));
1873
- const params = { user_ids, emails };
1874
- Promise.all([
1875
- new Promise((resolve) => {
1876
- checkOrigin()
1877
- .then((restParams) => fetch('api/users' + restParams))
1878
- .then(checkResponse)
1879
- .then((usersResponse) => {
1880
- resolve(usersResponse.data.managers);
1881
- });
1882
- }),
1883
- new Promise((resolve) => {
1884
- AP.validateUsers(params, (res) => {
1885
- resolve(res.data);
1495
+ function fillSettingsForm() {
1496
+ Object.entries(config.current).forEach(([key, value]) => {
1497
+ const el = settingsFieldRefs.current[key];
1498
+ if (el && (value || el.tagName.toLowerCase() === 'crowdin-checkbox')) {
1499
+ if (el.tagName.toLowerCase() === 'crowdin-select') {
1500
+ if (el.hasAttribute('is-multi')) {
1501
+ el.value = JSON.stringify(value);
1502
+ }
1503
+ else {
1504
+ el.value = JSON.stringify([value]);
1505
+ }
1506
+ }
1507
+ else if (el.tagName.toLowerCase() === 'crowdin-checkbox') {
1508
+ el.checked = !!value;
1509
+ }
1510
+ else if (el.tagName.toLowerCase() === 'crowdin-textarea') {
1511
+ el.value = value;
1512
+ el.setValue(value);
1513
+ }
1514
+ else {
1515
+ el.value = value;
1516
+ }
1517
+ triggerEvent(el, 'change');
1518
+ }
1519
+ });
1520
+ setTimeout(refreshDependencies, 100);
1521
+ }
1522
+ async function saveSettings() {
1523
+ var _a, _b, _c, _d;
1524
+ setIsLoading(true);
1525
+ const configReq = {};
1526
+ configurationFields === null || configurationFields === void 0 ? void 0 : configurationFields.forEach((tab) => {
1527
+ tab.fields.forEach((field) => {
1528
+ if (!field.key) {
1529
+ return;
1530
+ }
1531
+ const el = settingsFieldRefs.current[field.key];
1532
+ if (!el) {
1533
+ return;
1534
+ }
1535
+ const tagName = el.tagName.toLowerCase();
1536
+ if (tagName === 'crowdin-select') {
1537
+ const value = parseCrowdinSelectValue(el.value);
1538
+ configReq[field.key] = el.hasAttribute('is-multi') ? value : value[0];
1539
+ return;
1540
+ }
1541
+ if (tagName === 'crowdin-checkbox') {
1542
+ configReq[field.key] = !!el.checked;
1543
+ return;
1544
+ }
1545
+ configReq[field.key] = el.value;
1546
+ });
1547
+ });
1548
+ let isValidationError = false;
1549
+ let isSettingsSaveJobPending = false;
1550
+ let isSettingsSaveFailed = false;
1551
+ setIsSettingsSaveDisabled(true);
1552
+ try {
1553
+ const restParams = await checkOrigin();
1554
+ const res = await checkResponse(await fetch('api/settings' + restParams, {
1555
+ method: 'POST',
1556
+ headers: { 'Content-Type': 'application/json' },
1557
+ body: JSON.stringify({ config: configReq }),
1558
+ }));
1559
+ if (res === null || res === void 0 ? void 0 : res.jobId) {
1560
+ isSettingsSaveJobPending = true;
1561
+ checkJob({
1562
+ jobId: res.jobId,
1563
+ jobType: JOB_TYPE.integrationSettingsSave,
1564
+ onSuccess: () => {
1565
+ showToast('Settings successfully saved');
1566
+ config.current = configReq;
1567
+ refreshAutoSyncPausedIndicatorsAndWarning().catch((e) => catchRejection(e, "Can't fetch sync settings warning"));
1568
+ },
1569
+ onError: () => {
1570
+ isSettingsSaveFailed = true;
1571
+ showToast("Can't save settings");
1572
+ },
1573
+ onFinally: () => {
1574
+ setTimeout(() => setIsLoading(false), 500);
1575
+ setIsSettingsSaveDisabled(false);
1576
+ if (!isValidationError && !isSettingsSaveFailed) {
1577
+ closeModal(settingsModalRef.current);
1578
+ if (reloadOnConfigSave) {
1579
+ getIntegrationData({ hardReload: true });
1580
+ getCrowdinData();
1581
+ }
1582
+ }
1583
+ },
1886
1584
  });
1887
- }),
1888
- new Promise((resolve) => {
1889
- AP.getUsers((res) => {
1890
- resolve(res);
1585
+ return;
1586
+ }
1587
+ showToast('Settings successfully saved');
1588
+ config.current = configReq;
1589
+ refreshAutoSyncPausedIndicatorsAndWarning().catch((e) => catchRejection(e, "Can't fetch sync settings warning"));
1590
+ }
1591
+ catch (e) {
1592
+ if ((e === null || e === void 0 ? void 0 : e.error) && ((_a = e === null || e === void 0 ? void 0 : e.error) === null || _a === void 0 ? void 0 : _a.type) === 'validation_error') {
1593
+ Object.keys((_b = e === null || e === void 0 ? void 0 : e.error) === null || _b === void 0 ? void 0 : _b.details).forEach((key) => {
1594
+ var _a;
1595
+ const el = settingsFieldRefs.current[key];
1596
+ if (el) {
1597
+ el.setAttribute('error', 'true');
1598
+ el.setAttribute('error-text', (_a = e === null || e === void 0 ? void 0 : e.error) === null || _a === void 0 ? void 0 : _a.details[key]);
1599
+ }
1891
1600
  });
1892
- }),
1893
- ])
1894
- .then(([appManagers, checkUsersResponse, allUsers]) => {
1895
- const newUserEmails = checkUsersResponse.newUserEmails.map((email) => ({ name: email }));
1896
- const newProjectManagers = [
1897
- ...newUserEmails,
1898
- ...checkUsersResponse.newProjectManagers.map((user) => ({
1899
- name: getUserFullName(user),
1900
- })),
1901
- ];
1902
- const newApplicationAccessUsers = [
1903
- ...newUserEmails,
1904
- ...checkUsersResponse.newApplicationAccessUsers.map((user) => ({
1905
- name: getUserFullName(user),
1906
- })),
1907
- ];
1908
- const newApplicationManagers = [
1909
- ...newUserEmails,
1910
- ...Object.values(allUsers)
1911
- .filter((user) => users.some((id) => +id === user.id) && !appManagers.some((id) => +id === user.id))
1912
- .map((user) => ({ name: getUserFullName(user) })),
1913
- ];
1914
- prepareUsersConfirmBlock({
1915
- ...checkUsersResponse,
1916
- newUserEmails,
1917
- newProjectManagers,
1918
- newApplicationAccessUsers,
1919
- newApplicationManagers,
1920
- });
1921
- })
1922
- .catch((e) => catchRejection(e, (e === null || e === void 0 ? void 0 : e.error) || "Can't invite users"))
1923
- .finally(() => unsetLoader('#permissions-modal'));
1924
- }
1925
- function inviteUsers() {
1926
- setLoader('#permissions-modal');
1927
- const select = usersSelectRef.current;
1928
- const users = JSON.parse(select.value);
1929
- const user_ids = users.filter((id) => parseInt(id));
1930
- const emails = users.filter((id) => !parseInt(id));
1931
- const params = { user_ids, emails };
1932
- AP.inviteUsers(params, (res) => {
1933
- var _a;
1934
- if (!res.success) {
1935
- catchRejection(res, ((_a = res === null || res === void 0 ? void 0 : res.error) === null || _a === void 0 ? void 0 : _a.message) || "Can't invite users");
1936
- unsetLoader('#permissions-modal');
1601
+ isValidationError = true;
1602
+ showToast(((_d = (_c = e === null || e === void 0 ? void 0 : e.error) === null || _c === void 0 ? void 0 : _c.details) === null || _d === void 0 ? void 0 : _d.message) || "Can't save settings");
1603
+ setIsSettingsSaveDisabled(false);
1937
1604
  return;
1938
1605
  }
1939
- checkOrigin()
1940
- .then((restParams) => fetch('api/invite-users' + restParams, {
1941
- method: 'POST',
1942
- headers: { 'Content-Type': 'application/json' },
1943
- body: JSON.stringify(res.data),
1944
- }))
1945
- .then(checkResponse)
1946
- .then((res) => {
1947
- if (!res.success) {
1948
- catchRejection(res, (res === null || res === void 0 ? void 0 : res.message) || "Can't invite users");
1949
- return;
1606
+ catchRejection(e, "Can't save settings");
1607
+ }
1608
+ finally {
1609
+ if (isSettingsSaveJobPending) {
1610
+ return;
1611
+ }
1612
+ setTimeout(() => setIsLoading(false), 500);
1613
+ setIsSettingsSaveDisabled(false);
1614
+ if (!isValidationError) {
1615
+ closeModal(settingsModalRef.current);
1616
+ if (reloadOnConfigSave) {
1617
+ getIntegrationData({ hardReload: true });
1618
+ getCrowdinData();
1950
1619
  }
1951
- showToast('Users successfully updated');
1952
- hideConfirmUsersBlock();
1953
- closeModal(permissionsModalRef.current);
1954
- })
1955
- .catch((e) => catchRejection(e, (e === null || e === void 0 ? void 0 : e.error) || "Can't invite users"))
1956
- .finally(() => unsetLoader('#permissions-modal'));
1957
- });
1620
+ }
1621
+ }
1958
1622
  }
1959
- function prepareUsersConfirmBlock(usersData) {
1960
- showConfirmUsersBlock();
1961
- const toNames = (users = []) => users.map((user) => user.name);
1962
- const applicationSettingsDescription = 'This can be configured in organization settings (Apps section).';
1963
- const hasApplicationAccessUsers = usersData.newApplicationAccessUsers.length > 0;
1964
- setPermissionsSummary({
1965
- ...(hasOrganization
1966
- ? {
1967
- organization: {
1968
- status: usersData.newUserEmails.length
1969
- ? usersData.inviteRestricted
1970
- ? 'actionRequired'
1971
- : 'willBeRegistered'
1972
- : 'granted',
1973
- users: toNames(usersData.newUserEmails),
1974
- description: usersData.inviteRestricted
1975
- ? 'New user invitations are restricted to administrators.'
1976
- : '',
1977
- isWarning: usersData.inviteRestricted,
1978
- },
1623
+ return {
1624
+ isLoading,
1625
+ isSettingsSaveDisabled,
1626
+ fillSettingsForm,
1627
+ saveSettings,
1628
+ registerSettingsField,
1629
+ settingsFieldChange,
1630
+ };
1631
+ }
1632
+
1633
+ /// <reference path="../../../../ui/types.d.ts" />
1634
+ function useSyncOperations({ appComponent, asyncProgressRef, config, name, integrationOneLevelFetching, integrationSearchListener, integrationPagination, withCronSync, webhooks, progressiveCrowdinFilesLoading, asyncProgress, syncNewElements, }) {
1635
+ const crowdinFilesToUpdateTargetLanguages = reactExports.useRef([]);
1636
+ const asyncJobs = reactExports.useRef({});
1637
+ const project = reactExports.useRef({});
1638
+ const crowdinData = reactExports.useRef([]);
1639
+ const fileToSync = reactExports.useRef([]);
1640
+ const hasLoadMoreElements = reactExports.useRef(false);
1641
+ const savedIntegrationData = reactExports.useRef(null);
1642
+ const currentOpenedFolders = reactExports.useRef(null);
1643
+ const languageModal = reactExports.useRef(null);
1644
+ const languageSelectRef = reactExports.useRef(null);
1645
+ const [languageModalState, setLanguageModalState] = reactExports.useState(null);
1646
+ const silentJobs = [JOB_TYPE.integrationSettingsSave];
1647
+ reactExports.useEffect(() => {
1648
+ var _a;
1649
+ if (!languageModalState || !((_a = languageSelectRef.current) === null || _a === void 0 ? void 0 : _a.setValue)) {
1650
+ return;
1651
+ }
1652
+ languageSelectRef.current.setValue(languageModalState.languages.map((language) => language.id));
1653
+ }, [languageModalState]);
1654
+ reactExports.useEffect(() => {
1655
+ const listeners = {
1656
+ refreshFilesList: (e) => {
1657
+ if (e.detail.refreshIntegration) {
1658
+ getIntegrationData({ hardReload: true });
1659
+ }
1660
+ else if (e.detail.refreshCrowdin) {
1661
+ getCrowdinData();
1979
1662
  }
1980
- : {}),
1981
- project: {
1982
- status: usersData.newProjectManagers.length ? 'willBeGranted' : 'granted',
1983
- users: toNames(usersData.newProjectManagers),
1984
1663
  },
1985
- applicationSettings: {
1986
- status: hasApplicationAccessUsers
1987
- ? usersData.isAdmin
1988
- ? 'willBeGranted'
1989
- : 'actionRequired'
1990
- : 'granted',
1991
- users: toNames(usersData.newApplicationAccessUsers),
1992
- description: hasApplicationAccessUsers
1993
- ? usersData.isAdmin
1994
- ? applicationSettingsDescription
1995
- : `${applicationSettingsDescription} Only organization admins have permission to update this setting.`
1996
- : applicationSettingsDescription,
1997
- isWarning: hasApplicationAccessUsers && !usersData.isAdmin,
1664
+ crowdinFilesFolderToggled: (event) => {
1665
+ if (event.detail.componentId === 'crowdin-files' && event.detail.isOpen && event.detail.type === '1') {
1666
+ getFileProgress(event.detail.id);
1667
+ }
1668
+ if (integrationOneLevelFetching) {
1669
+ if (event.detail.componentId === 'integration-files' && event.detail.isOpen) {
1670
+ getIntegrationData({ parentId: event.detail.id, recursion: event.detail.recursion });
1671
+ }
1672
+ }
1998
1673
  },
1999
- applicationCredentials: {
2000
- status: usersData.newApplicationManagers.length ? 'willBeGranted' : 'granted',
2001
- users: toNames(usersData.newApplicationManagers),
1674
+ uploadFilesToCrowdin: uploadFilesToCrowdin,
1675
+ uploadFilesToIntegration: uploadFilesToIntegration,
1676
+ cancelAsyncAction: (e) => {
1677
+ cancelJob(e.detail);
2002
1678
  },
2003
- });
1679
+ loadMoreClick: async (e) => {
1680
+ var _a;
1681
+ const loadMoreFile = e.detail;
1682
+ const fileComponent = (_a = appComponent.current) === null || _a === void 0 ? void 0 : _a.querySelector('#integration-files');
1683
+ if (!fileComponent) {
1684
+ return;
1685
+ }
1686
+ await fileComponent.setLoadMoreLoading(loadMoreFile.id, true);
1687
+ try {
1688
+ await getIntegrationData({ parentId: e.detail.parent_id, paginationData: e.detail.pagination });
1689
+ }
1690
+ finally {
1691
+ await fileComponent.setLoadMoreLoading(loadMoreFile.id, false);
1692
+ }
1693
+ },
1694
+ };
1695
+ if (integrationSearchListener) {
1696
+ listeners['integrationFilterChange'] = async (event) => {
1697
+ const isEmptySearch = !event.detail || event.detail.trim() === '';
1698
+ if (isEmptySearch && savedIntegrationData.current && hasLoadMoreElements.current) {
1699
+ appComponent.current.setIntegrationFilesData(savedIntegrationData.current);
1700
+ appComponent.current.setIntegrationOpenedFolders(currentOpenedFolders.current);
1701
+ return;
1702
+ }
1703
+ if (!isEmptySearch && hasLoadMoreElements.current) {
1704
+ savedIntegrationData.current = await appComponent.current.getIntegrationFilesData();
1705
+ currentOpenedFolders.current = await appComponent.current.getIntegrationOpenedFolders();
1706
+ }
1707
+ getIntegrationData({ search: event.detail });
1708
+ };
1709
+ }
1710
+ if (integrationPagination) {
1711
+ listeners['fileListEnd'] = (e) => {
1712
+ getIntegrationData({ page: e.detail.page });
1713
+ };
1714
+ }
1715
+ for (const [event, handler] of Object.entries(listeners)) {
1716
+ document.body.addEventListener(event, handler);
1717
+ }
1718
+ getCrowdinData();
1719
+ getIntegrationData({});
1720
+ getActiveJobs();
1721
+ return () => {
1722
+ for (const [event, handler] of Object.entries(listeners)) {
1723
+ document.body.removeEventListener(event, handler);
1724
+ }
1725
+ };
1726
+ }, []);
1727
+ function transformCrowdinItemToTree(e) {
1728
+ const item = {
1729
+ parent_id: e.parentId ? e.parentId : '0',
1730
+ name: e.name,
1731
+ id: e.id,
1732
+ customContent: e.customContent,
1733
+ labels: e.labels,
1734
+ disabled: e.disabled,
1735
+ tooltip: e.tooltip,
1736
+ };
1737
+ if (e.type) {
1738
+ item.type = e.type;
1739
+ item.node_type = FILE_TYPE;
1740
+ item.failed = e.failed;
1741
+ item.excludedTargetLanguages = e.excludedTargetLanguages;
1742
+ }
1743
+ else {
1744
+ item.node_type = e.nodeType || FOLDER_TYPE;
1745
+ }
1746
+ return item;
1747
+ }
1748
+ async function getCrowdinData() {
1749
+ var _a, _b, _c, _d, _f;
1750
+ (_a = appComponent.current) === null || _a === void 0 ? void 0 : _a.setAttribute('is-crowdin-loading', 'true');
1751
+ try {
1752
+ const restParams = await checkOrigin();
1753
+ if (progressiveCrowdinFilesLoading) {
1754
+ const dirPromise = fetch('api/crowdin/files' + restParams + '&mode=directories')
1755
+ .then(checkResponse)
1756
+ .then((directories) => {
1757
+ var _a;
1758
+ const dirTree = directories.map(transformCrowdinItemToTree);
1759
+ crowdinData.current = dirTree;
1760
+ (_a = appComponent.current) === null || _a === void 0 ? void 0 : _a.setCrowdinFilesData(dirTree);
1761
+ return dirTree;
1762
+ });
1763
+ const [dirTree, files, projectData] = await Promise.all([
1764
+ dirPromise,
1765
+ fetch('api/crowdin/files' + restParams + '&mode=files').then(checkResponse),
1766
+ fetch('api/crowdin/project' + restParams).then(checkResponse),
1767
+ ]);
1768
+ const fileTree = files.map(transformCrowdinItemToTree);
1769
+ crowdinData.current = [...dirTree, ...fileTree];
1770
+ (_b = appComponent.current) === null || _b === void 0 ? void 0 : _b.setCrowdinFilesData(crowdinData.current);
1771
+ project.current = projectData;
1772
+ const languagesSorted = project.current.targetLanguages
1773
+ .map((language) => ({
1774
+ sourceLanguageId: projectData.sourceLanguage.editorCode,
1775
+ projectEditorLink: projectData.projectEditorLink,
1776
+ ...language,
1777
+ }))
1778
+ .sort((a, b) => a.name.localeCompare(b.name));
1779
+ if (project.current.inContext && ((_c = config.current) === null || _c === void 0 ? void 0 : _c.inContext)) {
1780
+ languagesSorted.push({ ...project.current.inContextPseudoLanguage, inContext: true });
1781
+ }
1782
+ setLanguagesForTranslation(languagesSorted);
1783
+ (_d = appComponent.current) === null || _d === void 0 ? void 0 : _d.setCrowdinLanguagesData(languagesSorted);
1784
+ }
1785
+ else {
1786
+ const [files, projectData] = await Promise.all([
1787
+ fetch('api/crowdin/files' + restParams).then(checkResponse),
1788
+ fetch('api/crowdin/project' + restParams).then(checkResponse),
1789
+ ]);
1790
+ const tree = files.map(transformCrowdinItemToTree);
1791
+ crowdinData.current = tree;
1792
+ appComponent.current.setCrowdinFilesData(tree);
1793
+ project.current = projectData;
1794
+ const languagesSorted = project.current.targetLanguages
1795
+ .map((language) => ({
1796
+ sourceLanguageId: projectData.sourceLanguage.editorCode,
1797
+ projectEditorLink: projectData.projectEditorLink,
1798
+ ...language,
1799
+ }))
1800
+ .sort((a, b) => a.name.localeCompare(b.name));
1801
+ if (project.current.inContext && ((_f = config.current) === null || _f === void 0 ? void 0 : _f.inContext)) {
1802
+ languagesSorted.push({ ...project.current.inContextPseudoLanguage, inContext: true });
1803
+ }
1804
+ setLanguagesForTranslation(languagesSorted);
1805
+ appComponent.current.setCrowdinLanguagesData(languagesSorted);
1806
+ }
1807
+ if (withCronSync || webhooks) {
1808
+ await getSyncSettings('crowdin');
1809
+ }
1810
+ }
1811
+ catch (e) {
1812
+ catchRejection(e, "Can't fetch Crowdin data");
1813
+ }
1814
+ finally {
1815
+ appComponent.current.setAttribute('is-crowdin-loading', false);
1816
+ }
1817
+ }
1818
+ async function getIntegrationData({ hardReload = false, parentId = '', search = '', page = 0, recursion = false, paginationData } = {}, recursionState = null) {
1819
+ if (!paginationData) {
1820
+ appComponent.current.setAttribute('is-integration-loading', true);
1821
+ }
1822
+ let recursionStateLocal = recursionState;
1823
+ try {
1824
+ const restParams = await checkOrigin();
1825
+ let url = `api/integration/data${restParams}&parent_id=${encodeURIComponent(parentId)}&search=${encodeURIComponent(search)}&page=${page}`;
1826
+ if (paginationData) {
1827
+ url += `&paginationData=${encodeURIComponent(JSON.stringify(paginationData))}`;
1828
+ }
1829
+ const res = await checkResponse(await fetch(url));
1830
+ const files = res.data;
1831
+ const stopPagination = res.stopPagination;
1832
+ const tree = files.map((e) => {
1833
+ const item = {
1834
+ parent_id: e.parentId ? e.parentId : '0',
1835
+ name: e.name,
1836
+ id: e.id,
1837
+ customContent: e.customContent,
1838
+ labels: e.labels,
1839
+ disabled: e.disabled,
1840
+ tooltip: e.tooltip,
1841
+ pagination: e.pagination,
1842
+ };
1843
+ if (e.type) {
1844
+ item.isNew = e.isNew;
1845
+ item.isUpdated = e.isUpdated;
1846
+ item.notSynced = e.notSynced;
1847
+ item.synced = e.synced;
1848
+ item.type = e.type;
1849
+ item.node_type = FILE_TYPE;
1850
+ }
1851
+ else {
1852
+ item.node_type = e.nodeType || FOLDER_TYPE;
1853
+ }
1854
+ return item;
1855
+ });
1856
+ if ((!paginationData && !parentId && !search) || hardReload) {
1857
+ hasLoadMoreElements.current = tree.some((item) => item.node_type === 'load-more');
1858
+ }
1859
+ const appIntegrationFiles = appComponent.current.querySelector('#integration-files');
1860
+ if (hardReload) {
1861
+ appComponent.current.setIntegrationFilesData(tree);
1862
+ }
1863
+ else if (tree.length) {
1864
+ appComponent.current.pushIntegrationFilesData(tree).then(async () => {
1865
+ if (parentId) {
1866
+ const selection = await appIntegrationFiles.getSelected();
1867
+ const selectedIds = selection === null || selection === void 0 ? void 0 : selection.filter((node) => node).map(({ id }) => id.toString());
1868
+ const filteredNodes = tree.filter((node) => !selectedIds.includes(node.id.toString()) &&
1869
+ selectedIds.includes(node.parent_id.toString()));
1870
+ if (filteredNodes === null || filteredNodes === void 0 ? void 0 : filteredNodes.length) {
1871
+ const filteredNodesId = filteredNodes.map(({ id }) => id.toString());
1872
+ await appIntegrationFiles.setSelected(filteredNodesId);
1873
+ }
1874
+ }
1875
+ });
1876
+ }
1877
+ if (integrationPagination) {
1878
+ if (stopPagination) {
1879
+ appIntegrationFiles.setAttribute('load-more-disabled', true);
1880
+ }
1881
+ else {
1882
+ appIntegrationFiles.setAttribute('load-more-disabled', false);
1883
+ }
1884
+ }
1885
+ if (search) {
1886
+ const openIds = files.filter((e) => !e.type).map((e) => e.id);
1887
+ appComponent.current.setIntegrationOpenedFolders(openIds);
1888
+ }
1889
+ if (recursion) {
1890
+ if (!recursionStateLocal) {
1891
+ recursionStateLocal = {
1892
+ openedFolders: new Set(),
1893
+ calls: 0,
1894
+ setLoading(isLoading) {
1895
+ appComponent.current.setAttribute('is-integration-loading', isLoading);
1896
+ },
1897
+ };
1898
+ recursionStateLocal.setLoading(true);
1899
+ }
1900
+ if (parentId) {
1901
+ recursionStateLocal.openedFolders.add(parentId);
1902
+ }
1903
+ const folderItems = files.filter(isFolder);
1904
+ if (folderItems.length === 0) {
1905
+ return;
1906
+ }
1907
+ folderItems.forEach((folder) => recursionStateLocal.openedFolders.add(folder.id));
1908
+ const openedFoldersNow = await appComponent.current.getIntegrationOpenedFolders();
1909
+ appComponent.current.setIntegrationOpenedFolders([
1910
+ ...openedFoldersNow,
1911
+ ...recursionStateLocal.openedFolders,
1912
+ ]);
1913
+ await Promise.all(folderItems.map(async (folder) => {
1914
+ recursionStateLocal.calls++;
1915
+ try {
1916
+ await getIntegrationData({ parentId: folder.id, recursion: true }, recursionState);
1917
+ }
1918
+ finally {
1919
+ recursionStateLocal.calls--;
1920
+ if (recursionStateLocal.calls === 0) {
1921
+ recursionStateLocal.setLoading(false);
1922
+ }
1923
+ }
1924
+ }));
1925
+ }
1926
+ if (withCronSync || webhooks) {
1927
+ await getSyncSettings('integration');
1928
+ }
1929
+ }
1930
+ catch (e) {
1931
+ catchRejection(e, `Can't fetch ${name} templates`);
1932
+ }
1933
+ finally {
1934
+ if (!recursionStateLocal && !recursion) {
1935
+ appComponent.current.setAttribute('is-integration-loading', false);
1936
+ }
1937
+ else if (recursionStateLocal && recursionStateLocal.calls === 0) {
1938
+ appComponent.current.setAttribute('is-integration-loading', false);
1939
+ }
1940
+ }
1941
+ }
1942
+ function isScheduleDisabled(currentConfig = config.current) {
1943
+ var _a;
1944
+ return String((_a = currentConfig === null || currentConfig === void 0 ? void 0 : currentConfig.schedule) !== null && _a !== void 0 ? _a : '0') === '0';
2004
1945
  }
2005
- function setLoader(id) {
2006
- const loaderKey = id.replace(/^#/, '');
2007
- setModalLoaders((currentLoaders) => ({ ...currentLoaders, [loaderKey]: true }));
1946
+ async function applyAutoSyncPausedMarkers(provider, syncSettingsFiles = []) {
1947
+ var _a, _b, _c, _d;
1948
+ const isDisabled = isScheduleDisabled();
1949
+ const configuredIds = new Set((Array.isArray(syncSettingsFiles) ? syncSettingsFiles : []).map((item) => `${item.id}`));
1950
+ const getChangedItems = (tree = []) => tree
1951
+ .map((item) => {
1952
+ const nextScheduleDisabled = Boolean(item.schedule) && isDisabled && configuredIds.has(`${item.id}`);
1953
+ const currentScheduleDisabled = Boolean(item.scheduleDisabled);
1954
+ if (currentScheduleDisabled === nextScheduleDisabled) {
1955
+ return null;
1956
+ }
1957
+ return { ...item, scheduleDisabled: nextScheduleDisabled };
1958
+ })
1959
+ .filter(Boolean);
1960
+ if (provider === 'crowdin') {
1961
+ const changedItems = getChangedItems(crowdinData.current || []);
1962
+ if (changedItems.length) {
1963
+ const changedById = new Map(changedItems.map((item) => [`${item.id}`, item]));
1964
+ crowdinData.current = (crowdinData.current || []).map((item) => changedById.get(`${item.id}`) || item);
1965
+ (_a = appComponent.current) === null || _a === void 0 ? void 0 : _a.updateCrowdinFileData(changedItems);
1966
+ }
1967
+ return;
1968
+ }
1969
+ const integrationData = await ((_c = (_b = appComponent.current) === null || _b === void 0 ? void 0 : _b.getIntegrationFilesData) === null || _c === void 0 ? void 0 : _c.call(_b));
1970
+ if (Array.isArray(integrationData)) {
1971
+ const changedItems = getChangedItems(integrationData);
1972
+ if (changedItems.length) {
1973
+ (_d = appComponent.current) === null || _d === void 0 ? void 0 : _d.updateIntegrationFileData(changedItems);
1974
+ }
1975
+ }
2008
1976
  }
2009
- function unsetLoader(id) {
2010
- const loaderKey = id.replace(/^#/, '');
2011
- setTimeout(function () {
2012
- setModalLoaders((currentLoaders) => ({ ...currentLoaders, [loaderKey]: false }));
2013
- }, 500);
1977
+ async function fetchSyncSettingsFiles(provider) {
1978
+ const restParams = await checkOrigin();
1979
+ const res = await fetch(`/api/sync-settings/${provider}` + restParams).then(checkResponse);
1980
+ return Array.isArray(res) ? res : [];
2014
1981
  }
2015
- function triggerEvent(el, type) {
2016
- const e = document.createEvent('HTMLEvents');
2017
- e.initEvent(type, false, true);
2018
- el.dispatchEvent(e);
1982
+ async function refreshAutoSyncPausedIndicatorsAndWarning() {
1983
+ const [crowdinSyncSettings, integrationSyncSettings] = await Promise.all([
1984
+ fetchSyncSettingsFiles('crowdin'),
1985
+ fetchSyncSettingsFiles('integration'),
1986
+ ]);
1987
+ await applyAutoSyncPausedMarkers('crowdin', crowdinSyncSettings);
1988
+ await applyAutoSyncPausedMarkers('integration', integrationSyncSettings);
1989
+ if (isScheduleDisabled() && (crowdinSyncSettings.length || integrationSyncSettings.length)) {
1990
+ showToast('Auto Sync is configured for some files, but sync schedule is disabled in app settings.');
1991
+ }
2019
1992
  }
2020
- function parseCrowdinSelectValue(value) {
2021
- if (Array.isArray(value)) {
2022
- return value;
1993
+ async function getSyncSettings(provider) {
1994
+ try {
1995
+ const restParams = await checkOrigin();
1996
+ const res = await checkResponse(await fetch(`/api/sync-settings/${provider}` + restParams));
1997
+ if (provider === 'crowdin') {
1998
+ if (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.crowdin) {
1999
+ appComponent.current.setCrowdinScheduleSync(res, true, true);
2000
+ }
2001
+ else {
2002
+ appComponent.current.setCrowdinScheduleSync(res);
2003
+ }
2004
+ }
2005
+ else {
2006
+ if (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration) {
2007
+ appComponent.current.setIntegrationScheduleSync(res, true, true);
2008
+ }
2009
+ else {
2010
+ appComponent.current.setIntegrationScheduleSync(res);
2011
+ }
2012
+ }
2013
+ await applyAutoSyncPausedMarkers(provider, res);
2023
2014
  }
2024
- if (value === undefined || value === null || value === '') {
2025
- return [];
2015
+ catch (e) {
2016
+ catchRejection(e, "Can't fetch file progress");
2026
2017
  }
2018
+ }
2019
+ async function getFileProgress(fileId) {
2027
2020
  try {
2028
- const parsedValue = JSON.parse(value);
2029
- return Array.isArray(parsedValue) ? parsedValue : [parsedValue];
2021
+ const restParams = await checkOrigin();
2022
+ const res = await checkResponse(await fetch(`api/crowdin/file-progress/${fileId}` + restParams));
2023
+ appComponent.current.addCrowdinFileProgress(res);
2030
2024
  }
2031
2025
  catch (e) {
2032
- return [value];
2026
+ catchRejection(e, "Can't fetch file progress");
2033
2027
  }
2034
2028
  }
2035
- function registerSettingsField(key) {
2036
- return (element) => {
2037
- if (!key) {
2038
- return;
2039
- }
2040
- if (element) {
2041
- settingsFieldRefs.current[key] = element;
2042
- }
2043
- else {
2044
- delete settingsFieldRefs.current[key];
2045
- }
2046
- };
2047
- }
2048
- function syncScheduleCheckbox(checkbox, { checked, hidden = false, clearSelection = false, }) {
2049
- if (!checkbox) {
2029
+ async function uploadFilesToCrowdin(event, languages = []) {
2030
+ var _a, _b, _c, _d, _f;
2031
+ let files = [];
2032
+ let uploadTranslations = false;
2033
+ let forcePushSources = false;
2034
+ if (((_a = event.detail) === null || _a === void 0 ? void 0 : _a.action) === 'uploadTranslations') {
2035
+ files = event.detail.files;
2036
+ uploadTranslations = true;
2037
+ forcePushSources = true;
2038
+ }
2039
+ else if (((_b = event.detail) === null || _b === void 0 ? void 0 : _b.action) === 'excludedTargetLanguages') {
2040
+ fileToSync.current = event.detail.files;
2041
+ openLanguageModal();
2050
2042
  return;
2051
2043
  }
2052
- checkbox.checked = checked;
2053
- checkbox.value = checked;
2054
- if (hidden) {
2055
- checkbox.setAttribute('hidden', '');
2044
+ else if (((_c = event.detail) === null || _c === void 0 ? void 0 : _c.action) === 'forcePushSources') {
2045
+ files = event.detail.files;
2046
+ forcePushSources = true;
2047
+ }
2048
+ else if (((_d = event.detail) === null || _d === void 0 ? void 0 : _d.action) === 'syncTranslationsToIntegration') {
2049
+ syncTranslationsToIntegration(event.detail.files);
2050
+ return;
2056
2051
  }
2057
2052
  else {
2058
- checkbox.removeAttribute('hidden');
2053
+ files = event.detail;
2059
2054
  }
2060
- if (clearSelection) {
2061
- checkbox.setAttribute('clear-selection', '');
2055
+ if (languages.length > 0) {
2056
+ forcePushSources = true;
2057
+ }
2058
+ if (((_f = event.detail) === null || _f === void 0 ? void 0 : _f.length) === 0 || !event.detail) {
2059
+ showToast('Select templates which will be pushed to Crowdin');
2060
+ return;
2061
+ }
2062
+ appComponent.current.setAttribute('is-to-crowdin-process', true);
2063
+ let req;
2064
+ if (integrationOneLevelFetching) {
2065
+ req = files.map((f) => ({
2066
+ name: f.name,
2067
+ id: f.id,
2068
+ type: f.type,
2069
+ parentId: f.parent_id,
2070
+ nodeType: f.node_type,
2071
+ isUpdated: f.isUpdated,
2072
+ }));
2062
2073
  }
2063
2074
  else {
2064
- checkbox.removeAttribute('clear-selection');
2075
+ req = files
2076
+ .filter((f) => f.node_type !== FOLDER_TYPE)
2077
+ .map((f) => ({
2078
+ name: f.name,
2079
+ id: f.id,
2080
+ type: f.type,
2081
+ parentId: f.parent_id,
2082
+ isUpdated: f.isUpdated,
2083
+ }));
2084
+ }
2085
+ try {
2086
+ const restParams = await checkOrigin();
2087
+ const res = await checkResponse(await fetch(`api/crowdin/update${restParams}&uploadTranslations=${uploadTranslations}&languages=${languages}&forcePushSources=${forcePushSources}`, {
2088
+ method: 'POST',
2089
+ headers: { 'Content-Type': 'application/json' },
2090
+ body: JSON.stringify(req),
2091
+ }));
2092
+ checkJob({ jobId: res === null || res === void 0 ? void 0 : res.jobId, jobType: JOB_TYPE.updateCrowdin });
2093
+ }
2094
+ catch (e) {
2095
+ catchRejection(e, "Can't upload templates to Crowdin");
2065
2096
  }
2066
2097
  }
2067
- function fillSettingsForm() {
2068
- Object.entries(config.current).forEach(([key, value]) => {
2069
- const el = settingsFieldRefs.current[key];
2070
- if (el && (value || el.tagName.toLowerCase() === 'crowdin-checkbox')) {
2071
- if (el.tagName.toLowerCase() === 'crowdin-select') {
2072
- if (el.hasAttribute('is-multi')) {
2073
- el.value = JSON.stringify(value);
2074
- }
2075
- else {
2076
- el.value = JSON.stringify([value]);
2077
- }
2078
- }
2079
- else if (el.tagName.toLowerCase() === 'crowdin-checkbox') {
2080
- el.checked = !!value;
2081
- }
2082
- else if (el.tagName.toLowerCase() === 'crowdin-textarea') {
2083
- el.value = value;
2084
- el.setValue(value);
2085
- }
2086
- else {
2087
- el.value = value;
2088
- }
2089
- triggerEvent(el, 'change');
2090
- }
2091
- });
2092
- setTimeout(refreshDependencies, 100);
2093
- }
2094
- function saveSettings() {
2095
- setLoader('#settings-modal');
2096
- const configReq = {};
2097
- configurationFields === null || configurationFields === void 0 ? void 0 : configurationFields.forEach((tab) => {
2098
- tab.fields.forEach((field) => {
2099
- if (!field.key) {
2100
- return;
2101
- }
2102
- const el = settingsFieldRefs.current[field.key];
2103
- if (!el) {
2104
- return;
2105
- }
2106
- const tagName = el.tagName.toLowerCase();
2107
- if (tagName === 'crowdin-select') {
2108
- const value = parseCrowdinSelectValue(el.value);
2109
- configReq[field.key] = el.hasAttribute('is-multi') ? value : value[0];
2110
- return;
2111
- }
2112
- if (tagName === 'crowdin-checkbox') {
2113
- configReq[field.key] = !!el.checked;
2114
- return;
2115
- }
2116
- configReq[field.key] = el.value;
2117
- });
2118
- });
2119
- let isValidationError = false;
2120
- let isSettingsSaveJobPending = false;
2121
- let isSettingsSaveFailed = false;
2122
- setIsSettingsSaveDisabled(true);
2123
- checkOrigin()
2124
- .then((restParams) => fetch('api/settings' + restParams, {
2125
- method: 'POST',
2126
- headers: { 'Content-Type': 'application/json' },
2127
- body: JSON.stringify({ config: configReq }),
2128
- }))
2129
- .then(checkResponse)
2130
- .then((res) => {
2131
- if (res === null || res === void 0 ? void 0 : res.jobId) {
2132
- isSettingsSaveJobPending = true;
2133
- checkJob({
2134
- jobId: res.jobId,
2135
- jobType: JOB_TYPE.integrationSettingsSave,
2136
- onSuccess: () => {
2137
- showToast('Settings successfully saved');
2138
- config.current = configReq;
2139
- refreshAutoSyncPausedIndicatorsAndWarning().catch((e) => catchRejection(e, "Can't fetch sync settings warning"));
2140
- },
2141
- onError: () => {
2142
- isSettingsSaveFailed = true;
2143
- showToast("Can't save settings");
2144
- },
2145
- onFinally: () => {
2146
- unsetLoader('#settings-modal');
2147
- setIsSettingsSaveDisabled(false);
2148
- if (!isValidationError && !isSettingsSaveFailed) {
2149
- closeModal(settingsModalRef.current);
2150
- if (reloadOnConfigSave) {
2151
- getIntegrationData({ hardReload: true });
2152
- getCrowdinData();
2153
- }
2154
- }
2155
- },
2156
- });
2157
- return;
2158
- }
2159
- showToast('Settings successfully saved');
2160
- config.current = configReq;
2161
- refreshAutoSyncPausedIndicatorsAndWarning().catch((e) => catchRejection(e, "Can't fetch sync settings warning"));
2162
- })
2163
- .catch((e) => {
2164
- var _a, _b, _c, _d;
2165
- if ((e === null || e === void 0 ? void 0 : e.error) && ((_a = e === null || e === void 0 ? void 0 : e.error) === null || _a === void 0 ? void 0 : _a.type) === 'validation_error') {
2166
- Object.keys((_b = e === null || e === void 0 ? void 0 : e.error) === null || _b === void 0 ? void 0 : _b.details).forEach((key) => {
2167
- var _a;
2168
- const el = settingsFieldRefs.current[key];
2169
- if (el) {
2170
- el.setAttribute('error', 'true');
2171
- el.setAttribute('error-text', (_a = e === null || e === void 0 ? void 0 : e.error) === null || _a === void 0 ? void 0 : _a.details[key]);
2172
- }
2173
- });
2174
- isValidationError = true;
2175
- showToast(((_d = (_c = e === null || e === void 0 ? void 0 : e.error) === null || _c === void 0 ? void 0 : _c.details) === null || _d === void 0 ? void 0 : _d.message) || "Can't save settings");
2176
- setIsSettingsSaveDisabled(false);
2177
- return;
2178
- }
2179
- catchRejection(e, "Can't save settings");
2180
- })
2181
- .finally(() => {
2182
- if (isSettingsSaveJobPending) {
2098
+ async function getActiveJobs() {
2099
+ try {
2100
+ const restParams = await checkOrigin();
2101
+ const jobs = await checkResponse(await fetch(`api/jobs${restParams}`));
2102
+ if (!Array.isArray(jobs)) {
2183
2103
  return;
2184
2104
  }
2185
- unsetLoader('#settings-modal');
2186
- setIsSettingsSaveDisabled(false);
2187
- if (!isValidationError) {
2188
- closeModal(settingsModalRef.current);
2189
- if (reloadOnConfigSave) {
2190
- getIntegrationData({ hardReload: true });
2191
- getCrowdinData();
2105
+ jobs.forEach((job) => {
2106
+ switch (job.type) {
2107
+ case JOB_TYPE.updateCrowdin:
2108
+ case JOB_TYPE.updateTargetLanguages:
2109
+ appComponent.current.setAttribute('is-to-crowdin-process', true);
2110
+ break;
2111
+ case JOB_TYPE.updateIntegration:
2112
+ appComponent.current.setAttribute('is-to-integration-process', true);
2113
+ break;
2114
+ case JOB_TYPE.crowdinSyncSettingsSave:
2115
+ appComponent.current.setAttribute('is-crowdin-sync-settings-in-progress', true);
2116
+ break;
2117
+ case JOB_TYPE.integrationSyncSettingsSave:
2118
+ appComponent.current.setAttribute('is-integration-sync-settings-in-progress', true);
2119
+ break;
2120
+ default:
2192
2121
  }
2193
- }
2194
- });
2122
+ checkJob({ jobId: job.id, jobType: job.type });
2123
+ });
2124
+ }
2125
+ catch (e) {
2126
+ catchRejection(e, 'Sync status check failed');
2127
+ }
2195
2128
  }
2196
- function getIntegrationFoldersToExpand(allData, syncedFiles) {
2197
- return Array.isArray(allData)
2198
- ? allData
2199
- .filter((node) => node.node_type === folderType &&
2200
- !syncedFiles.find((syncedFile) => syncedFile.parent_id === node.id))
2201
- .map((node) => ({
2202
- ...node,
2203
- nodeType: node.node_type,
2204
- parentId: node.parent_id,
2205
- }))
2206
- : [];
2129
+ async function cancelJob(jobId) {
2130
+ var _a;
2131
+ if ((_a = asyncJobs.current[jobId]) === null || _a === void 0 ? void 0 : _a.isFailed) {
2132
+ return;
2133
+ }
2134
+ try {
2135
+ const restParams = await checkOrigin();
2136
+ await checkResponse(await fetch('api/jobs' + restParams + '&jobId=' + jobId, {
2137
+ method: 'DELETE',
2138
+ headers: { 'Content-Type': 'application/json' },
2139
+ }));
2140
+ showToast('Cancellation…');
2141
+ }
2142
+ catch (e) {
2143
+ catchRejection(e, 'Sync cancellation failed');
2144
+ }
2207
2145
  }
2208
- async function saveScheduleSync() {
2209
- const { type, newFiles: newFile, selectedFiles, allFolderFiles } = scheduleModalState;
2210
- if (type === 'crowdin') {
2211
- const crowdinFiles = await appComponent.current.getCrowdinFilesData();
2212
- const syncedFileIds = Object.keys(syncData.current);
2213
- const crowdinFolderIds = crowdinFiles
2214
- .filter((file) => file.node_type === folderType || file.node_type === branchType)
2215
- .filter((file) => syncedFileIds.includes(String(file.id)))
2216
- .map((file) => String(file.id));
2217
- appComponent.current.setCrowdinScheduleSync(syncData.current, newFile, selectedFiles);
2218
- appComponent.current.setCrowdinFilesHasSyncOption(true);
2219
- const syncedFiles = await appComponent.current.getCrowdinScheduleSync(true);
2220
- appComponent.current.setAttribute('is-crowdin-sync-settings-in-progress', true);
2221
- updateSyncSettings(syncedFiles, 'schedule', 'crowdin', [], crowdinFolderIds);
2146
+ async function checkJob({ jobId, jobType, onSuccess, onError, onFinally }) {
2147
+ switch (jobType) {
2148
+ case JOB_TYPE.updateCrowdin:
2149
+ case JOB_TYPE.updateTargetLanguages:
2150
+ if (!onFinally) {
2151
+ onFinally = () => {
2152
+ appComponent.current.setAttribute('is-to-crowdin-process', false);
2153
+ getCrowdinData();
2154
+ };
2155
+ }
2156
+ break;
2157
+ case JOB_TYPE.updateIntegration:
2158
+ if (!onFinally) {
2159
+ onFinally = () => appComponent.current.setAttribute('is-to-integration-process', false);
2160
+ }
2161
+ break;
2162
+ case JOB_TYPE.crowdinSyncSettingsSave:
2163
+ if (!onFinally) {
2164
+ onFinally = () => appComponent.current.setAttribute('is-crowdin-sync-settings-in-progress', false);
2165
+ }
2166
+ break;
2167
+ case JOB_TYPE.integrationSyncSettingsSave:
2168
+ if (!onFinally) {
2169
+ onFinally = () => appComponent.current.setAttribute('is-integration-sync-settings-in-progress', false);
2170
+ }
2171
+ break;
2222
2172
  }
2223
- else if (type === 'integration') {
2224
- if (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration) {
2225
- appComponent.current.setIntegrationScheduleSync(syncData.current, newFile, selectedFiles);
2173
+ try {
2174
+ const restParams = await checkOrigin();
2175
+ const job = await checkResponse(await fetch('api/jobs' + restParams + '&jobId=' + jobId));
2176
+ const isFailed = [JOB_STATUS.failed, JOB_STATUS.canceled].includes(job.status);
2177
+ const progress = isFailed || JOB_STATUS.finished === job.status ? 100 : job.progress;
2178
+ const info = JOB_STATUS.canceled === job.status ? `Cancelled\n${job.info || ''}` : job.info;
2179
+ if (!silentJobs.find((type) => type === jobType)) {
2180
+ pushJobs([{ id: job.id, title: job.title, progress, info, isFailed }]);
2226
2181
  }
2227
- else {
2228
- appComponent.current.setIntegrationScheduleSync(syncData.current);
2182
+ if (isFailed) {
2183
+ if (onError) {
2184
+ onError();
2185
+ }
2229
2186
  }
2230
- appComponent.current.setIntegrationFilesHasSyncOption(true);
2231
- const syncedFiles = await appComponent.current.getIntegrationScheduleSync(true);
2232
- appComponent.current.setAttribute('is-integration-sync-settings-in-progress', true);
2233
- if (integrationOneLevelFetching) {
2234
- updateSyncSettings(syncedFiles, 'schedule', 'integration', selectedFiles ? getIntegrationFoldersToExpand(syncData.current, syncedFiles) : []);
2187
+ else if ([JOB_STATUS.created, JOB_STATUS.inProgress].includes(job.status) && job.progress < 100) {
2188
+ setTimeout(() => checkJob({ jobId, jobType, onSuccess, onError, onFinally }), asyncProgress.checkInterval);
2189
+ return;
2235
2190
  }
2236
2191
  else {
2237
- if (allFolderFiles) {
2238
- const integrationExpandedFolders = syncData.current
2239
- .filter((file) => file.node_type === folderType)
2240
- .map((file) => ({
2241
- ...file,
2242
- nodeType: file.node_type,
2243
- parentId: file.parent_id,
2244
- }));
2245
- updateSyncSettings(syncedFiles, 'schedule', 'integration', integrationExpandedFolders);
2246
- }
2247
- else {
2248
- updateSyncSettings(syncedFiles, 'schedule', 'integration');
2192
+ if (onSuccess) {
2193
+ onSuccess(job);
2249
2194
  }
2250
2195
  }
2251
- }
2252
- closeModal(scheduleModalRef.current);
2253
- }
2254
- async function openScheduleModal(type, options = {}) {
2255
- var _a, _b, _c, _d;
2256
- const { showAllFolderFiles = false, showNewFiles = true } = options;
2257
- let newFileStatus = 'unChecked';
2258
- const integrationSyncFolders = ((await ((_b = (_a = appComponent.current) === null || _a === void 0 ? void 0 : _a.getIntegrationScheduleSync) === null || _b === void 0 ? void 0 : _b.call(_a, true))) || []).filter((elements) => elements.node_type === '0');
2259
- const integrationFiles = (_c = appComponent.current) === null || _c === void 0 ? void 0 : _c.querySelector('#integration-files');
2260
- const selectedIntegrationFolders = ((await ((_d = integrationFiles === null || integrationFiles === void 0 ? void 0 : integrationFiles.getSelected) === null || _d === void 0 ? void 0 : _d.call(integrationFiles, true))) || []).filter((elements) => elements.node_type === '0');
2261
- for (let i = 0; i < selectedIntegrationFolders.length; i++) {
2262
- const selectedElement = selectedIntegrationFolders[i];
2263
- const found = integrationSyncFolders.some((integrationElement) => integrationElement.id === selectedElement.id);
2264
- if (found) {
2265
- newFileStatus = 'checked';
2196
+ if (onFinally) {
2197
+ onFinally(job);
2266
2198
  }
2267
- else if (!found && newFileStatus === 'checked') {
2268
- newFileStatus = 'clear-selection';
2269
- break;
2199
+ }
2200
+ catch (_e) {
2201
+ if (onFinally) {
2202
+ onFinally();
2270
2203
  }
2271
- else {
2272
- newFileStatus = 'unChecked';
2204
+ if (!silentJobs.find((type) => type === jobType)) {
2205
+ pushJobs([{ id: jobId, isFailed: true }]);
2273
2206
  }
2207
+ showToast('Sync status check failed');
2274
2208
  }
2275
- setScheduleModalState({
2276
- type,
2277
- showAllFolderFiles,
2278
- showNewFiles,
2279
- newFiles: showNewFiles && newFileStatus === 'checked',
2280
- newFilesClearSelection: showNewFiles && newFileStatus === 'clear-selection',
2281
- selectedFiles: true,
2282
- allFolderFiles: false,
2283
- });
2284
- setIsScheduleSaveDisabled(false);
2285
- openModal(scheduleModalRef.current);
2286
- }
2287
- function onChangeAutoSynchronizationOptions() {
2288
- var _a, _b, _c;
2289
- const nextState = {
2290
- newFiles: scheduleModalState.showNewFiles ? !!((_a = newFilesCheckboxRef.current) === null || _a === void 0 ? void 0 : _a.checked) : false,
2291
- selectedFiles: !!((_b = selectedFilesCheckboxRef.current) === null || _b === void 0 ? void 0 : _b.checked),
2292
- allFolderFiles: scheduleModalState.showAllFolderFiles
2293
- ? !!((_c = allFolderFilesCheckboxRef.current) === null || _c === void 0 ? void 0 : _c.checked)
2294
- : false,
2295
- };
2296
- setScheduleModalState((currentState) => ({
2297
- ...currentState,
2298
- ...nextState,
2299
- newFilesClearSelection: false,
2300
- }));
2301
- setIsScheduleSaveDisabled(!nextState.newFiles && !nextState.selectedFiles && !nextState.allFolderFiles);
2302
2209
  }
2303
- async function hasFolder(selectedFiles) {
2304
- let isFolder;
2305
- if (Array.isArray(selectedFiles)) {
2306
- isFolder = selectedFiles.find((file) => file.node_type === folderType || file.node_type === branchType);
2210
+ function pushJobs(jobs) {
2211
+ const el = asyncProgressRef.current;
2212
+ if (el && el.pushJobs) {
2213
+ el.pushJobs(jobs);
2214
+ jobs.forEach((job) => (asyncJobs.current[job.id] = job));
2307
2215
  }
2308
2216
  else {
2309
- const files = await appComponent.current.getCrowdinFilesData();
2310
- const folders = files.filter((file) => file.node_type === folderType || file.node_type === branchType);
2311
- isFolder = folders.find((folder) => selectedFiles.hasOwnProperty(folder.id));
2217
+ setTimeout(() => pushJobs(jobs), 300);
2312
2218
  }
2313
- return isFolder !== undefined;
2314
2219
  }
2315
- async function setIntegrationScheduleSync(e) {
2316
- var _a;
2317
- if (e.detail.length === 0) {
2318
- showToast('Select templates which will be pushed to Crowdin');
2220
+ async function uploadFilesToIntegration(event) {
2221
+ var _a, _b, _c, _d;
2222
+ let files = {};
2223
+ let forcePushTranslations = false;
2224
+ if (((_a = event.detail) === null || _a === void 0 ? void 0 : _a.action) === 'forcePushTranslations') {
2225
+ files = event.detail.files;
2226
+ forcePushTranslations = true;
2227
+ }
2228
+ else if (((_b = event.detail) === null || _b === void 0 ? void 0 : _b.action) === 'excludedTargetLanguages') {
2229
+ crowdinFilesToUpdateTargetLanguages.current = event.detail.files;
2230
+ openLanguageModalForCrowdinFiles();
2319
2231
  return;
2320
2232
  }
2321
- let syncedFiles;
2322
- if (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration) {
2323
- const newIntegrationFiles = (_a = config.current) === null || _a === void 0 ? void 0 : _a['new-integration-files'];
2324
- syncData.current = e.detail;
2325
- const isFolder = await hasFolder(e.detail);
2326
- const shouldShowModal = isFolder && !newIntegrationFiles;
2327
- const showNewFiles = true; // завжди показуємо New files коли є syncNewElements.integration
2328
- const showAllFolderFiles = hasLoadMoreElements.current; // показуємо All files якщо є hasLoadMoreElements
2329
- if (shouldShowModal) {
2330
- await openScheduleModal('integration', { showNewFiles, showAllFolderFiles });
2331
- return;
2332
- }
2333
- appComponent.current.setIntegrationScheduleSync(e.detail);
2334
- appComponent.current.setIntegrationFilesHasSyncOption(true);
2335
- syncedFiles = await appComponent.current.getIntegrationScheduleSync(true);
2233
+ else if (((_c = event.detail) === null || _c === void 0 ? void 0 : _c.action) === 'syncSourcesToCrowdin') {
2234
+ syncToCrowdinFromCrowdinFiles(event.detail.files, false);
2235
+ return;
2236
+ }
2237
+ else if (((_d = event.detail) === null || _d === void 0 ? void 0 : _d.action) === 'syncTranslationsToCrowdin') {
2238
+ syncToCrowdinFromCrowdinFiles(event.detail.files, true);
2239
+ return;
2336
2240
  }
2337
2241
  else {
2338
- syncData.current = e.detail;
2339
- const isFolder = await hasFolder(e.detail);
2340
- if (hasLoadMoreElements.current && isFolder) {
2341
- await openScheduleModal('integration', { showNewFiles: false, showAllFolderFiles: true });
2342
- return;
2343
- }
2344
- syncedFiles = await appComponent.current.getIntegrationScheduleSync();
2242
+ files = event.detail;
2345
2243
  }
2346
- appComponent.current.setAttribute('is-integration-sync-settings-in-progress', true);
2347
- if (integrationOneLevelFetching) {
2348
- updateSyncSettings(syncedFiles, 'schedule', 'integration', getIntegrationFoldersToExpand(e.detail, syncedFiles));
2244
+ if (Object.keys(event.detail).length === 0 || !event.detail) {
2245
+ showToast(`Select files which will be uploaded to ${name}`);
2246
+ return;
2349
2247
  }
2350
- else {
2351
- if (hasLoadMoreElements.current) {
2352
- const integrationExpandedFolders = e.detail
2353
- .filter((file) => file.node_type === folderType)
2354
- .map((file) => ({
2355
- ...file,
2356
- nodeType: file.node_type,
2357
- parentId: file.parent_id,
2358
- }));
2359
- updateSyncSettings(syncedFiles, 'schedule', 'integration', integrationExpandedFolders);
2360
- }
2361
- else {
2362
- updateSyncSettings(syncedFiles, 'schedule', 'integration');
2363
- }
2248
+ appComponent.current.setAttribute('is-to-integration-process', true);
2249
+ const req = {};
2250
+ Object.keys(files)
2251
+ .filter((id) => crowdinData.current.find((c) => c.id === id).node_type === FILE_TYPE)
2252
+ .forEach((id) => (req[id] = files[id]));
2253
+ try {
2254
+ const restParams = await checkOrigin();
2255
+ const res = await checkResponse(await fetch(`api/integration/update${restParams}&forcePushTranslations=${forcePushTranslations}`, {
2256
+ method: 'POST',
2257
+ headers: { 'Content-Type': 'application/json' },
2258
+ body: JSON.stringify(req),
2259
+ }));
2260
+ checkJob({ jobId: res === null || res === void 0 ? void 0 : res.jobId, jobType: JOB_TYPE.updateIntegration });
2261
+ }
2262
+ catch (e) {
2263
+ catchRejection(e, `Can't upload files to ${name}`);
2364
2264
  }
2365
2265
  }
2366
- async function setCrowdinScheduleSync(e) {
2367
- var _a;
2368
- if (e.detail.length === 0) {
2369
- showToast('Select templates which will be pushed to Crowdin');
2266
+ async function syncToCrowdinFromCrowdinFiles(files, uploadTranslations) {
2267
+ if (!files || Object.keys(files).length === 0) {
2268
+ showToast('Select files to sync');
2370
2269
  return;
2371
2270
  }
2372
- let syncedFiles;
2373
- if (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.crowdin) {
2374
- const newCrowdinFiles = (_a = config.current) === null || _a === void 0 ? void 0 : _a['new-crowdin-files'];
2375
- syncData.current = e.detail;
2376
- const isFolder = await hasFolder(e.detail);
2377
- if (isFolder && !newCrowdinFiles) {
2378
- await openScheduleModal('crowdin');
2379
- return;
2380
- }
2381
- appComponent.current.setCrowdinScheduleSync(e.detail);
2382
- appComponent.current.setCrowdinFilesHasSyncOption(true);
2383
- syncedFiles = await appComponent.current.getCrowdinScheduleSync(true);
2384
- }
2385
- else {
2386
- syncedFiles = await appComponent.current.getCrowdinScheduleSync();
2271
+ appComponent.current.setAttribute('is-to-crowdin-process', true);
2272
+ const req = {};
2273
+ Object.keys(files)
2274
+ .filter((id) => { var _a; return ((_a = crowdinData.current.find((c) => c.id === id)) === null || _a === void 0 ? void 0 : _a.node_type) === FILE_TYPE; })
2275
+ .forEach((id) => (req[id] = files[id]));
2276
+ try {
2277
+ const restParams = await checkOrigin();
2278
+ const res = await checkResponse(await fetch(`api/crowdin/update${restParams}&matchFromCrowdin=true&uploadTranslations=${uploadTranslations}`, {
2279
+ method: 'POST',
2280
+ headers: { 'Content-Type': 'application/json' },
2281
+ body: JSON.stringify(req),
2282
+ }));
2283
+ checkJob({ jobId: res === null || res === void 0 ? void 0 : res.jobId, jobType: JOB_TYPE.updateCrowdin });
2284
+ }
2285
+ catch (e) {
2286
+ catchRejection(e, "Can't sync files to Crowdin");
2387
2287
  }
2388
- appComponent.current.setAttribute('is-crowdin-sync-settings-in-progress', true);
2389
- updateSyncSettings(syncedFiles, 'schedule', 'crowdin');
2390
2288
  }
2391
- async function disableIntegrationSync(e) {
2392
- if (e.detail.length === 0) {
2393
- showToast('Select templates which will be pushed to Crowdin');
2289
+ async function syncTranslationsToIntegration(files) {
2290
+ if (!files || files.length === 0) {
2291
+ showToast('Select files to sync');
2394
2292
  return;
2395
2293
  }
2396
- appComponent.current.setAttribute('is-integration-sync-settings-in-progress', true);
2397
- let syncedFiles;
2398
- if (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration) {
2399
- syncedFiles = await appComponent.current.getIntegrationScheduleSync(true);
2294
+ appComponent.current.setAttribute('is-to-integration-process', true);
2295
+ const req = files
2296
+ .filter((f) => f.node_type !== FOLDER_TYPE)
2297
+ .map((f) => ({ name: f.name, id: f.id, type: f.type, parentId: f.parent_id }));
2298
+ try {
2299
+ const restParams = await checkOrigin();
2300
+ const res = await checkResponse(await fetch(`api/integration/update${restParams}&matchFromIntegration=true`, {
2301
+ method: 'POST',
2302
+ headers: { 'Content-Type': 'application/json' },
2303
+ body: JSON.stringify(req),
2304
+ }));
2305
+ checkJob({ jobId: res === null || res === void 0 ? void 0 : res.jobId, jobType: JOB_TYPE.updateIntegration });
2400
2306
  }
2401
- else {
2402
- syncedFiles = await appComponent.current.getIntegrationScheduleSync();
2307
+ catch (e) {
2308
+ catchRejection(e, `Can't sync translations to ${name}`);
2403
2309
  }
2404
- updateSyncSettings(syncedFiles, 'schedule', 'integration');
2405
2310
  }
2406
- async function disableCrowdinSync(e) {
2407
- if (e.detail.length === 0) {
2408
- showToast('Select templates which will be pushed to Crowdin');
2311
+ function openLanguageModal() {
2312
+ setLanguageModalState((state) => {
2313
+ var _a, _b;
2314
+ return ({
2315
+ languages: (_b = (_a = state === null || state === void 0 ? void 0 : state.languages) !== null && _a !== void 0 ? _a : project.current.targetLanguages) !== null && _b !== void 0 ? _b : [],
2316
+ action: 'syncToCrowdin',
2317
+ });
2318
+ });
2319
+ openModal(languageModal.current);
2320
+ }
2321
+ function openLanguageModalForCrowdinFiles() {
2322
+ setLanguageModalState((state) => {
2323
+ var _a, _b;
2324
+ return ({
2325
+ languages: (_b = (_a = state === null || state === void 0 ? void 0 : state.languages) !== null && _a !== void 0 ? _a : project.current.targetLanguages) !== null && _b !== void 0 ? _b : [],
2326
+ action: 'updateCrowdinTargetLanguages',
2327
+ });
2328
+ });
2329
+ openModal(languageModal.current);
2330
+ }
2331
+ async function uploadFilesToCrowdinWithExcludedLanguages() {
2332
+ const selectedLanguages = await languageSelectRef.current.getValue();
2333
+ closeModal(languageModal.current);
2334
+ uploadFilesToCrowdin({ detail: fileToSync.current }, selectedLanguages);
2335
+ }
2336
+ async function updateCrowdinFilesTargetLanguages() {
2337
+ const selectedLanguages = await languageSelectRef.current.getValue();
2338
+ closeModal(languageModal.current);
2339
+ const fileIds = Object.keys(crowdinFilesToUpdateTargetLanguages.current).map((id) => parseInt(id, 10));
2340
+ if (fileIds.length === 0) {
2341
+ showToast('Select files to update');
2409
2342
  return;
2410
2343
  }
2411
- appComponent.current.setAttribute('is-crowdin-sync-settings-in-progress', true);
2412
- let syncedFiles;
2413
- if (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.crowdin) {
2414
- syncedFiles = await appComponent.current.getCrowdinScheduleSync(true);
2344
+ appComponent.current.setAttribute('is-to-crowdin-process', true);
2345
+ try {
2346
+ const restParams = await checkOrigin();
2347
+ const res = await checkResponse(await fetch(`api/crowdin/files-target-languages${restParams}`, {
2348
+ method: 'POST',
2349
+ headers: { 'Content-Type': 'application/json' },
2350
+ body: JSON.stringify({ fileIds, languages: selectedLanguages }),
2351
+ }));
2352
+ checkJob({ jobId: res === null || res === void 0 ? void 0 : res.jobId, jobType: JOB_TYPE.updateTargetLanguages });
2415
2353
  }
2416
- else {
2417
- syncedFiles = await appComponent.current.getCrowdinScheduleSync();
2354
+ catch (e) {
2355
+ catchRejection(e, "Can't update files target languages");
2418
2356
  }
2419
- updateSyncSettings(syncedFiles, 'schedule', 'crowdin');
2420
2357
  }
2421
- function updateSyncSettings(files, type, provider, expandIntegrationFolders = [], folderIds = []) {
2422
- checkOrigin()
2423
- .then((restParams) => fetch('api/sync-settings' + restParams, {
2424
- method: 'POST',
2425
- headers: { 'Content-Type': 'application/json' },
2426
- body: JSON.stringify({
2427
- files,
2428
- type,
2429
- provider,
2430
- expandIntegrationFolders,
2431
- hasLoadMoreElements: hasLoadMoreElements.current,
2432
- folderIds,
2433
- }),
2434
- }))
2435
- .then(checkResponse)
2436
- .then((res) => {
2358
+ function setLanguagesForTranslation(languages, action = 'syncToCrowdin') {
2359
+ setLanguageModalState({ languages, action });
2360
+ }
2361
+ async function updateSyncSettings(files, type, provider, expandIntegrationFolders = [], folderIds = []) {
2362
+ try {
2363
+ const restParams = await checkOrigin();
2364
+ const res = await checkResponse(await fetch('api/sync-settings' + restParams, {
2365
+ method: 'POST',
2366
+ headers: { 'Content-Type': 'application/json' },
2367
+ body: JSON.stringify({
2368
+ files,
2369
+ type,
2370
+ provider,
2371
+ expandIntegrationFolders,
2372
+ hasLoadMoreElements: hasLoadMoreElements.current,
2373
+ folderIds,
2374
+ }),
2375
+ }));
2437
2376
  checkJob({
2438
2377
  jobId: res === null || res === void 0 ? void 0 : res.jobId,
2439
2378
  jobType: JOB_TYPE[`${provider}SyncSettingsSave`],
2440
2379
  });
2441
- })
2442
- .catch((e) => catchRejection(e, "Can't save schedule sync settings"));
2380
+ }
2381
+ catch (e) {
2382
+ catchRejection(e, "Can't save schedule sync settings");
2383
+ }
2384
+ }
2385
+ return {
2386
+ getCrowdinData,
2387
+ getIntegrationData,
2388
+ checkJob,
2389
+ cancelJob,
2390
+ getActiveJobs,
2391
+ updateSyncSettings,
2392
+ refreshAutoSyncPausedIndicatorsAndWarning,
2393
+ uploadFilesToCrowdin,
2394
+ uploadFilesToIntegration,
2395
+ updateCrowdinFilesTargetLanguages,
2396
+ uploadFilesToCrowdinWithExcludedLanguages,
2397
+ languageModalState,
2398
+ languageModalRef: languageModal,
2399
+ languageSelectRef,
2400
+ crowdinData,
2401
+ hasLoadMoreElements,
2402
+ };
2403
+ }
2404
+
2405
+ const ExcludedLanguagesModal = ({ modalRef, languageSelectRef, languageModalState, onUpdateTargetLanguages, onSyncToCrowdin, }) => {
2406
+ return (jsxRuntimeExports.jsx("crowdin-modal", { ref: modalRef, style: { display: 'none' }, "modal-width": "50", "modal-title": "Select Target Languages for Translation", id: "excluded-languages", children: languageModalState && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("div", { children: jsxRuntimeExports.jsx("crowdin-select", { ref: languageSelectRef, "is-multi": true, "is-searchable": true, "is-position-fixed": true, "close-on-select": "false", name: "languages", id: "language-select", label: "Languages", children: languageModalState.languages.map((language) => (jsxRuntimeExports.jsx("option", { value: language.id, children: language.name }, language.id))) }) }), jsxRuntimeExports.jsxs("div", { slot: "footer", children: [jsxRuntimeExports.jsx("span", { hidden: languageModalState.action !== 'updateCrowdinTargetLanguages', children: jsxRuntimeExports.jsx("crowdin-button", { id: "update-crowdin-target-languages", outlined: true, onClick: onUpdateTargetLanguages, children: "Update Target Languages" }) }), jsxRuntimeExports.jsx("span", { hidden: languageModalState.action !== 'syncToCrowdin', children: jsxRuntimeExports.jsx("crowdin-button", { id: "upload-exclude-languages", outlined: true, onClick: onSyncToCrowdin, children: jsxRuntimeExports.jsxs("div", { className: "inline-flex items-center gap-1", children: [jsxRuntimeExports.jsx("span", { children: "Sync to" }), jsxRuntimeExports.jsx("crowdin-logo", { "is-icon": true })] }) }) })] })] })) }));
2407
+ };
2408
+
2409
+ const InfoModal = ({ modalRef, infoModal }) => {
2410
+ return (jsxRuntimeExports.jsx("crowdin-modal", { ref: modalRef, style: { display: 'none' }, id: "info-modal", "modal-width": "50", "modal-title": infoModal.title, "close-button-title": "Close", children: jsxRuntimeExports.jsx("div", { dangerouslySetInnerHTML: { __html: infoModal.content } }) }));
2411
+ };
2412
+
2413
+ function renderPermissionStatus(status) {
2414
+ switch (status) {
2415
+ case 'willBeGranted':
2416
+ return (jsxRuntimeExports.jsx("span", { className: "inline-block cursor-default rounded-[15px] bg-[var(--crowdin-info)] px-3 py-1 text-center text-xs font-medium text-[var(--crowdin-white)]", children: "Will Be Granted" }));
2417
+ case 'willBeRegistered':
2418
+ return (jsxRuntimeExports.jsx("span", { className: "inline-block cursor-default rounded-[15px] bg-[var(--crowdin-info)] px-3 py-1 text-center text-xs font-medium text-[var(--crowdin-white)]", children: "Will Be Registered" }));
2419
+ case 'actionRequired':
2420
+ return (jsxRuntimeExports.jsx("span", { className: "inline-block cursor-default rounded-[15px] bg-[var(--crowdin-warning-bg)] px-3 py-1 text-center text-xs font-medium text-[var(--crowdin-warning)]", children: "Action Required" }));
2421
+ case 'granted':
2422
+ default:
2423
+ return jsxRuntimeExports.jsx("crowdin-p", { children: EMPTY_PERMISSION_VALUE });
2443
2424
  }
2444
- function getSubscriptionInfo() {
2445
- checkOrigin()
2446
- .then((restParams) => fetch('api/subscription-info' + restParams))
2447
- .then(checkResponse)
2448
- .then((res) => {
2449
- if (res.showInfo) {
2450
- subscriptionLink.current = res.subscribeLink;
2451
- setSubscriptionInfo({ daysLeft: res.daysLeft, subscribeLink: res.subscribeLink });
2452
- }
2425
+ }
2426
+ function renderPermissionUsers(row) {
2427
+ if (!row || row.users.length === 0) {
2428
+ return row ? renderPermissionStatus(row.status) : null;
2429
+ }
2430
+ return (jsxRuntimeExports.jsx("ul", { className: "m-0 list-none p-0", children: row.users.map((user) => (jsxRuntimeExports.jsx("li", { className: "mb-1 text-sm last:mb-0", children: jsxRuntimeExports.jsx("crowdin-p", { children: user }) }, user))) }));
2431
+ }
2432
+ function renderPermissionRowDescription(row) {
2433
+ if (!(row === null || row === void 0 ? void 0 : row.description)) {
2434
+ return null;
2435
+ }
2436
+ return (jsxRuntimeExports.jsx("div", { className: row.isWarning
2437
+ ? 'mt-1 text-xs text-[var(--crowdin-warning)]'
2438
+ : 'mt-1 text-xs text-[var(--crowdin-text-muted)]', children: row.description }));
2439
+ }
2440
+ const PermissionsModal = ({ modalRef, usersSelectRef, isLoading, permissionUserOptions, isConfirmVisible, permissionsSummary, hasOrganization, zenModeUrl, onSave, }) => {
2441
+ return (jsxRuntimeExports.jsxs("crowdin-modal", { ref: modalRef, style: { display: 'none' }, id: "permissions-modal", "modal-title": "Share", "close-button-title": "Close", "close-button": true, "body-overflow-unset": true, children: [jsxRuntimeExports.jsx("div", { className: isLoading ? 'absolute inset-0 z-[4] flex items-center justify-center bg-white/30' : 'hidden', children: jsxRuntimeExports.jsx("crowdin-progress-indicator", {}) }), jsxRuntimeExports.jsxs("div", { children: [jsxRuntimeExports.jsx("div", { children: jsxRuntimeExports.jsx("crowdin-users-select", { ref: usersSelectRef, "allow-new-options": true, "is-multi": true, "is-searchable": true, id: "users", label: "Users", "help-text": "Search for members by name, username, email, or invite new ones using their email.", "is-position-fixed": true, children: permissionUserOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.id, children: option.label }, option.id))) }, "users") }), jsxRuntimeExports.jsxs("div", { children: [jsxRuntimeExports.jsx("div", { className: isConfirmVisible ? 'mt-4' : 'hidden', children: jsxRuntimeExports.jsxs("table", { className: "w-full border-separate border-spacing-0", children: [jsxRuntimeExports.jsx("thead", { children: jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsx("th", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left text-sm", style: { width: '40%' }, children: "Access" }), jsxRuntimeExports.jsx("th", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left text-sm", style: { width: '35%' }, children: "Users" }), jsxRuntimeExports.jsx("th", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-right text-sm", style: { width: '20%' }, children: "Upcoming changes" })] }) }), jsxRuntimeExports.jsxs("tbody", { children: [hasOrganization && (jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsxs("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: [jsxRuntimeExports.jsx("crowdin-p", { children: "Registration in Organization" }), renderPermissionRowDescription(permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.organization)] }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: renderPermissionUsers(permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.organization) }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-right align-top", children: (permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.organization) &&
2442
+ renderPermissionStatus(permissionsSummary.organization.status) })] })), jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: jsxRuntimeExports.jsx("crowdin-p", { children: "Project Manager Access" }) }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: renderPermissionUsers(permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.project) }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-right align-top", children: (permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.project) &&
2443
+ renderPermissionStatus(permissionsSummary.project.status) })] }), jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsxs("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: [jsxRuntimeExports.jsx("crowdin-p", { children: "Application Visibility Access" }), renderPermissionRowDescription(permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.applicationSettings)] }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: renderPermissionUsers(permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.applicationSettings) }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-right align-top", children: (permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.applicationSettings) &&
2444
+ renderPermissionStatus(permissionsSummary.applicationSettings.status) })] }), jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsxs("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: [jsxRuntimeExports.jsx("crowdin-p", { children: "Access to the Integration" }), jsxRuntimeExports.jsx("div", { className: "mt-1 text-xs text-[var(--crowdin-text-muted)]", children: "This provides access to sync files and modify the current integration settings." })] }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: renderPermissionUsers(permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.applicationCredentials) }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-right align-top", children: (permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.applicationCredentials) &&
2445
+ renderPermissionStatus(permissionsSummary.applicationCredentials.status) })] })] })] }) }), zenModeUrl && (jsxRuntimeExports.jsx("div", { className: "mt-4", children: jsxRuntimeExports.jsx("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 }) }))] })] }), jsxRuntimeExports.jsx("div", { slot: "footer", children: jsxRuntimeExports.jsx("crowdin-button", { id: "confirm-users-btn", outlined: true, onClick: onSave, children: "Save" }) })] }));
2446
+ };
2447
+
2448
+ const ScheduleModal = ({ modalRef, selectedFilesCheckboxRef, allFolderFilesCheckboxRef, newFilesCheckboxRef, scheduleModalState, isSaveDisabled, onChangeOptions, onSave, }) => {
2449
+ return (jsxRuntimeExports.jsxs("crowdin-modal", { ref: modalRef, 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, children: [jsxRuntimeExports.jsx("crowdin-checkbox", { ref: selectedFilesCheckboxRef, id: "selected-files", name: "selected-files", label: "Selected files", class: "hydrated", checked: scheduleModalState.selectedFiles, value: String(scheduleModalState.selectedFiles), onChange: onChangeOptions }), jsxRuntimeExports.jsx("crowdin-checkbox", { ref: allFolderFilesCheckboxRef, id: "all-folder-files", name: "all-folder-files", label: "All files in the folder", class: "hydrated", hidden: !scheduleModalState.showAllFolderFiles, checked: scheduleModalState.allFolderFiles, value: String(scheduleModalState.allFolderFiles), onChange: onChangeOptions }), jsxRuntimeExports.jsx("crowdin-checkbox", { ref: newFilesCheckboxRef, id: "new-files", name: "new-files", label: "New files", class: "hydrated", hidden: !scheduleModalState.showNewFiles, checked: scheduleModalState.newFiles, value: String(scheduleModalState.newFiles), ...(scheduleModalState.newFilesClearSelection ? { 'clear-selection': '' } : {}), onChange: onChangeOptions }), jsxRuntimeExports.jsx("div", { slot: "footer", children: jsxRuntimeExports.jsx("crowdin-button", { outlined: true, id: "save-schedule-sync", disabled: isSaveDisabled, onClick: onSave, children: "Save" }) })] }));
2450
+ };
2451
+
2452
+ function renderConfigField(field, settingsFieldChange, registerSettingsField, suffix = 'settings') {
2453
+ var _a;
2454
+ if (!field.key) {
2455
+ return field.labelHtml ? (jsxRuntimeExports.jsx("crowdin-p", { ...(field.dependencySettings ? { 'data-dependency': field.dependencySettings } : {}), dangerouslySetInnerHTML: { __html: field.labelHtml } })) : (jsxRuntimeExports.jsx("crowdin-p", { ...(field.dependencySettings ? { 'data-dependency': field.dependencySettings } : {}), children: field.label }));
2456
+ }
2457
+ const commonProps = {
2458
+ id: `${field.key}-${suffix}`,
2459
+ _key: field.key,
2460
+ label: field.label,
2461
+ ...(field.helpText ? { 'help-text': field.helpText } : {}),
2462
+ ...(field.helpTextHtml ? { 'help-text-html': field.helpTextHtml } : {}),
2463
+ ...(field.dependencySettings ? { 'data-dependency': field.dependencySettings } : {}),
2464
+ };
2465
+ switch (field.type) {
2466
+ case 'checkbox':
2467
+ return (jsxRuntimeExports.jsx("crowdin-checkbox", { ref: registerSettingsField(field.key), ...commonProps, value: "false", "use-switch": true, ...(field.defaultValue === true ? { checked: String(field.defaultValue) } : {}), onChange: settingsFieldChange }));
2468
+ case 'select':
2469
+ return (jsxRuntimeExports.jsx("crowdin-select", { ref: registerSettingsField(field.key), ...commonProps, ...(field.isMulti ? { 'is-multi': true, 'close-on-select': 'false' } : {}), ...(field.isSearchable ? { 'is-searchable': true } : {}), "is-position-fixed": true, onChange: settingsFieldChange, children: (_a = field.options) === null || _a === void 0 ? void 0 : _a.map((option) => {
2470
+ const isSelected = Array.isArray(field.defaultValue)
2471
+ ? field.defaultValue.includes(option.value)
2472
+ : field.defaultValue === option.value;
2473
+ return (jsxRuntimeExports.jsx("option", { value: option.value, ...(isSelected ? { selected: true } : {}), children: option.label }, option.value));
2474
+ }) }));
2475
+ case 'text':
2476
+ return (jsxRuntimeExports.jsx("crowdin-input", { ref: registerSettingsField(field.key), ...commonProps, "with-fixed-height": true, value: field.defaultValue || '', onChange: settingsFieldChange }));
2477
+ case 'textarea':
2478
+ return (jsxRuntimeExports.jsx("crowdin-textarea", { ref: registerSettingsField(field.key), ...commonProps, value: field.defaultValue || '', onChange: settingsFieldChange }));
2479
+ case 'notice':
2480
+ return (jsxRuntimeExports.jsx("crowdin-alert", { ...(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.helpTextHtml || field.helpText || '' } }));
2481
+ default:
2482
+ return null;
2483
+ }
2484
+ }
2485
+ const SettingsModal = ({ modalRef, isLoading, configurationFields, isSaveDisabled, settingsFieldChange, registerSettingsField, onSave, }) => {
2486
+ return (jsxRuntimeExports.jsxs("crowdin-modal", { ref: modalRef, style: { display: 'none' }, id: "settings-modal", "modal-width": "65", "modal-title": "Settings", "close-button-title": "Close", children: [jsxRuntimeExports.jsx("div", { className: isLoading ? 'absolute inset-0 z-[4] flex items-center justify-center bg-white/30' : 'hidden', children: jsxRuntimeExports.jsx("crowdin-progress-indicator", {}) }), jsxRuntimeExports.jsx("div", { id: "modal-content", children: jsxRuntimeExports.jsx("crowdin-tabs", { "tabs-config": JSON.stringify(configurationFields.map((f) => f.name)), "active-tab": "1", children: configurationFields.map((tab, tabIndex) => (jsxRuntimeExports.jsx("div", { slot: `tab-${tabIndex + 1}`, children: jsxRuntimeExports.jsx("div", { children: tab.fields.map((field, fieldIndex) => (jsxRuntimeExports.jsxs(reactExports.Fragment, { children: [renderConfigField(field, settingsFieldChange, registerSettingsField), jsxRuntimeExports.jsx("div", { style: { padding: '8px' }, ...(field.dependencySettings
2487
+ ? { 'data-dependency': field.dependencySettings }
2488
+ : {}) })] }, field.key || `${tabIndex}-${fieldIndex}`))) }) }, tabIndex))) }) }), jsxRuntimeExports.jsx("div", { slot: "footer", children: jsxRuntimeExports.jsx("crowdin-button", { id: "settings-save-btn", outlined: true, disabled: isSaveDisabled, onClick: onSave, children: "Save" }) })] }));
2489
+ };
2490
+
2491
+ const SubscriptionModal = ({ modalRef, subscriptionLink, name, onLogout }) => {
2492
+ return (jsxRuntimeExports.jsxs("crowdin-modal", { ref: modalRef, style: { display: 'none' }, id: "subscription-modal", "modal-width": "50", "close-button": "false", children: [jsxRuntimeExports.jsx("div", { children: jsxRuntimeExports.jsxs("crowdin-alert", { type: "warning", children: ["Subscribe to continue using the ", name, " app."] }) }), jsxRuntimeExports.jsxs("div", { slot: "footer", children: [jsxRuntimeExports.jsx("crowdin-button", { primary: true, onClick: () => { var _a; return window.open((_a = subscriptionLink.current) !== null && _a !== void 0 ? _a : '', '_blank', 'noopener,noreferrer'); }, children: "Subscribe" }), jsxRuntimeExports.jsx("crowdin-button", { outlined: true, onClick: () => window.open('https://crowdin.com/contacts', '_blank', 'noopener,noreferrer'), children: "Contact us" }), jsxRuntimeExports.jsx("crowdin-button", { class: "ml-2.5", secondary: true, onClick: onLogout, children: "Log out" })] })] }));
2493
+ };
2494
+
2495
+ function isJSON(string) {
2496
+ try {
2497
+ JSON.parse(string);
2498
+ return true;
2499
+ }
2500
+ catch (e) {
2501
+ return false;
2502
+ }
2503
+ }
2504
+ function getUserErrorDetail(error) {
2505
+ const data = JSON.parse(error.data);
2506
+ const preClassName = 'block rounded bg-[#eceff1] p-4 text-[87.5%] text-[#212529]';
2507
+ const rows = [
2508
+ { label: 'Action', value: error.action },
2509
+ {
2510
+ label: 'Message',
2511
+ value: isJSON(error.message) ? jsxRuntimeExports.jsx("pre", { className: preClassName, children: error.message }) : error.message,
2512
+ },
2513
+ { label: 'Date/time', value: error.formattedDate },
2514
+ ];
2515
+ if (data.requestParams) {
2516
+ rows.push({ label: 'Method', value: data.requestParams.method });
2517
+ }
2518
+ if (data.responseData) {
2519
+ rows.push({
2520
+ label: 'Response Data',
2521
+ value: jsxRuntimeExports.jsx("pre", { className: preClassName, children: JSON.stringify(data.responseData, null, 2) }),
2453
2522
  });
2454
2523
  }
2524
+ if (data.appData) {
2525
+ if (Array.isArray(data.appData)) {
2526
+ rows.push({
2527
+ label: 'App Data',
2528
+ value: jsxRuntimeExports.jsx("pre", { className: preClassName, children: JSON.stringify(data.appData, null, 2) }),
2529
+ });
2530
+ }
2531
+ else if (typeof data.appData === 'object') {
2532
+ for (const key in data.appData) {
2533
+ rows.push({
2534
+ label: key.charAt(0).toUpperCase() + key.slice(1),
2535
+ value: jsxRuntimeExports.jsx("pre", { className: preClassName, children: JSON.stringify(data.appData[key], null, 2) }),
2536
+ });
2537
+ }
2538
+ }
2539
+ else {
2540
+ rows.push({ label: 'App Data', value: data.appData });
2541
+ }
2542
+ }
2543
+ return (jsxRuntimeExports.jsx("div", { className: "-m-6 h-auto max-h-[579px] overflow-auto", children: jsxRuntimeExports.jsx("table", { className: "w-full border-separate border-spacing-0", children: jsxRuntimeExports.jsx("tbody", { children: rows.map((row) => (jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsx("td", { className: "min-w-[200px] p-2 px-6 align-top font-medium", children: row.label }), jsxRuntimeExports.jsx("td", { className: "break-all bg-[var(--crowdin-primary-blue-gray,#f6f6f6)] p-2 px-6 text-[var(--crowdin-body-color,rgba(38,50,56,0.87))]", children: row.value })] }, row.label))) }) }) }));
2544
+ }
2545
+ const UserErrorDetailModal = ({ modalRef, selectedUserError }) => {
2546
+ return (jsxRuntimeExports.jsx("crowdin-modal", { ref: modalRef, style: { display: 'none' }, id: "user-error-detail", "close-button-title": "Close", "close-button": "true", children: selectedUserError && getUserErrorDetail(selectedUserError) }));
2547
+ };
2548
+
2549
+ const MainPage = (props) => {
2550
+ const { name, userId, notice, checkSubscription, isManager, isOwner, infoModal, configurationFields, config: configString, syncNewElements, withCronSync, webhooks, integrationOneLevelFetching, integrationSearchListener, integrationPagination, excludedTargetLanguages, filtering, hasOrganization, zenModeUrl, asyncProgress = { checkInterval: 3000 }, reloadOnConfigSave, progressiveCrowdinFilesLoading, integrationButtonMenuItems, crowdinButtonMenuItems, } = props;
2551
+ const config = reactExports.useRef(configString ? JSON.parse(configString.replace(/\\n/g, '\\\\n')) : {});
2552
+ const appComponent = reactExports.useRef(null);
2553
+ const asyncProgressRef = reactExports.useRef(null);
2554
+ const infoModalRef = reactExports.useRef(null);
2555
+ const subscriptionModal = reactExports.useRef(null);
2556
+ const settingsModalRef = reactExports.useRef(null);
2557
+ const scheduleModalRef = reactExports.useRef(null);
2558
+ const permissionsModalRef = reactExports.useRef(null);
2559
+ const userErrorsTableRef = reactExports.useRef(null);
2560
+ const userErrorDetailModalRef = reactExports.useRef(null);
2561
+ const subscriptionLink = reactExports.useRef('');
2562
+ const refreshDependencies = useFieldDependencies(configurationFields);
2563
+ const [isNoticeVisible, setIsNoticeVisible] = reactExports.useState(false);
2564
+ const [subscriptionInfo, setSubscriptionInfo] = reactExports.useState(null);
2565
+ const [isErrorLogsVisible, setIsErrorLogsVisible] = reactExports.useState(false);
2566
+ const [selectedUserError, setSelectedUserError] = reactExports.useState(null);
2567
+ const syncOps = useSyncOperations({
2568
+ appComponent,
2569
+ asyncProgressRef,
2570
+ config,
2571
+ name,
2572
+ integrationOneLevelFetching,
2573
+ integrationSearchListener,
2574
+ integrationPagination,
2575
+ withCronSync,
2576
+ webhooks,
2577
+ progressiveCrowdinFilesLoading,
2578
+ asyncProgress,
2579
+ syncNewElements,
2580
+ });
2581
+ const permissions = usePermissions({
2582
+ userId,
2583
+ hasOrganization,
2584
+ permissionsModalRef,
2585
+ });
2586
+ const settings = useSettings({
2587
+ configurationFields,
2588
+ config,
2589
+ settingsModalRef,
2590
+ reloadOnConfigSave,
2591
+ refreshDependencies,
2592
+ checkJob: syncOps.checkJob,
2593
+ getCrowdinData: syncOps.getCrowdinData,
2594
+ getIntegrationData: syncOps.getIntegrationData,
2595
+ refreshAutoSyncPausedIndicatorsAndWarning: syncOps.refreshAutoSyncPausedIndicatorsAndWarning,
2596
+ });
2597
+ const schedule = useSchedule({
2598
+ appComponent,
2599
+ config,
2600
+ scheduleModalRef,
2601
+ withCronSync,
2602
+ webhooks,
2603
+ syncNewElements,
2604
+ integrationOneLevelFetching,
2605
+ hasLoadMoreElements: syncOps.hasLoadMoreElements,
2606
+ updateSyncSettings: syncOps.updateSyncSettings,
2607
+ });
2608
+ reactExports.useEffect(() => {
2609
+ const handleKeydown = (event) => {
2610
+ if (event.keyCode === 27) {
2611
+ closeModal(permissionsModalRef.current);
2612
+ closeModal(infoModalRef.current);
2613
+ closeModal(settingsModalRef.current);
2614
+ closeModal(scheduleModalRef.current);
2615
+ if (syncOps.languageModalRef.current) {
2616
+ closeModal(syncOps.languageModalRef.current);
2617
+ }
2618
+ }
2619
+ };
2620
+ document.body.addEventListener('keydown', handleKeydown);
2621
+ return () => document.body.removeEventListener('keydown', handleKeydown);
2622
+ }, []);
2623
+ // Expose centralized API to global scope for main.js compatibility
2624
+ reactExports.useEffect(() => {
2625
+ window.app = {
2626
+ subscriptionModal,
2627
+ subscriptionLink,
2628
+ openModal,
2629
+ };
2630
+ return () => {
2631
+ delete window.app;
2632
+ };
2633
+ }, []);
2634
+ reactExports.useEffect(() => {
2635
+ setIsNoticeVisible(Boolean(notice) && localStorage.getItem('revised_notice') !== '1');
2636
+ if (checkSubscription) {
2637
+ getSubscriptionInfo();
2638
+ }
2639
+ }, []);
2640
+ async function getSubscriptionInfo() {
2641
+ const restParams = await checkOrigin();
2642
+ const res = await checkResponse(await fetch('api/subscription-info' + restParams));
2643
+ if (res.showInfo) {
2644
+ subscriptionLink.current = res.subscribeLink;
2645
+ setSubscriptionInfo({ daysLeft: res.daysLeft, subscribeLink: res.subscribeLink });
2646
+ }
2647
+ }
2648
+ async function integrationLogout() {
2649
+ try {
2650
+ const queryParams = await checkOrigin();
2651
+ await checkResponse(await fetch(`api/logout${queryParams}`, { method: 'POST' }));
2652
+ reloadLocation();
2653
+ localStorage.removeItem(`revised_${name}`);
2654
+ }
2655
+ catch (e) {
2656
+ catchRejection(e, e.error || 'Looks like you are not logged in');
2657
+ }
2658
+ }
2455
2659
  function closeAlert(suffix) {
2456
- const alertName = suffix ;
2457
2660
  setIsNoticeVisible(false);
2458
- localStorage.setItem(`revised_${alertName}`, '1');
2661
+ localStorage.setItem(`revised_${suffix !== null && suffix !== void 0 ? suffix : name}`, '1');
2459
2662
  }
2460
- function showErrorLogs() {
2663
+ async function showErrorLogs() {
2461
2664
  setIsErrorLogsVisible(true);
2462
- checkOrigin()
2463
- .then((restParams) => fetch('api/user-errors' + restParams))
2464
- .then(checkResponse)
2465
- .then((res) => {
2665
+ try {
2666
+ const restParams = await checkOrigin();
2667
+ const res = await checkResponse(await fetch('api/user-errors' + restParams));
2466
2668
  const table = userErrorsTableRef.current;
2467
- const clickRow = (field, index, item) => {
2669
+ const clickRow = (_field, _index, item) => {
2468
2670
  setSelectedUserError(item);
2469
2671
  openModal(userErrorDetailModalRef.current);
2470
2672
  };
2471
- const formatDate = (field, index, item) => {
2472
- return item.formattedDate;
2473
- };
2673
+ const formatDate = (_field, _index, item) => item.formattedDate;
2474
2674
  if (table.setTableConfig) {
2475
2675
  table.setTableConfig({
2476
2676
  defaultSortingColumn: 'createdAt',
@@ -2483,156 +2683,25 @@
2483
2683
  clickFn: clickRow,
2484
2684
  formatFn: formatDate,
2485
2685
  },
2486
- {
2487
- field: 'action',
2488
- name: 'Action',
2489
- clickFn: clickRow,
2490
- },
2491
- {
2492
- field: 'message',
2493
- name: 'Message',
2494
- clickFn: clickRow,
2495
- },
2686
+ { field: 'action', name: 'Action', clickFn: clickRow },
2687
+ { field: 'message', name: 'Message', clickFn: clickRow },
2496
2688
  ],
2497
2689
  });
2498
2690
  }
2499
2691
  table.setTableData(JSON.stringify(res));
2500
2692
  table.setAttribute('is-loading', false);
2501
- })
2502
- .catch((e) => catchRejection(e, "Can't fetch error logs"));
2503
- }
2504
- function showIntegration() {
2505
- setIsErrorLogsVisible(false);
2506
- }
2507
- function isJSON(string) {
2508
- try {
2509
- JSON.parse(string);
2510
- return true;
2511
2693
  }
2512
2694
  catch (e) {
2513
- return false;
2514
- }
2515
- }
2516
- function getUserErrorDetail(error) {
2517
- const data = JSON.parse(error.data);
2518
- const preClassName = 'block rounded bg-[#eceff1] p-4 text-[87.5%] text-[#212529]';
2519
- const rows = [
2520
- { label: 'Action', value: error.action },
2521
- {
2522
- label: 'Message',
2523
- value: isJSON(error.message) ? jsxRuntimeExports.jsx("pre", { className: preClassName, children: error.message }) : error.message,
2524
- },
2525
- { label: 'Date/time', value: error.formattedDate },
2526
- ];
2527
- if (data.requestParams) {
2528
- rows.push({ label: 'Method', value: data.requestParams.method });
2529
- }
2530
- if (data.responseData) {
2531
- rows.push({
2532
- label: 'Response Data',
2533
- value: jsxRuntimeExports.jsx("pre", { className: preClassName, children: JSON.stringify(data.responseData, null, 2) }),
2534
- });
2535
- }
2536
- if (data.appData) {
2537
- if (Array.isArray(data.appData)) {
2538
- rows.push({
2539
- label: 'App Data',
2540
- value: jsxRuntimeExports.jsx("pre", { className: preClassName, children: JSON.stringify(data.appData, null, 2) }),
2541
- });
2542
- }
2543
- else if (typeof data.appData === 'object') {
2544
- for (const key in data.appData) {
2545
- rows.push({
2546
- label: key.charAt(0).toUpperCase() + key.slice(1),
2547
- value: jsxRuntimeExports.jsx("pre", { className: preClassName, children: JSON.stringify(data.appData[key], null, 2) }),
2548
- });
2549
- }
2550
- }
2551
- else {
2552
- rows.push({ label: 'App Data', value: data.appData });
2553
- }
2554
- }
2555
- return (jsxRuntimeExports.jsx("div", { className: "-m-6 h-auto max-h-[579px] overflow-auto", children: jsxRuntimeExports.jsx("table", { className: "w-full border-separate border-spacing-0", children: jsxRuntimeExports.jsx("tbody", { children: rows.map((row) => (jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsx("td", { className: "min-w-[200px] p-2 px-6 align-top font-medium", children: row.label }), jsxRuntimeExports.jsx("td", { className: "break-all bg-[var(--crowdin-primary-blue-gray,#f6f6f6)] p-2 px-6 text-[var(--crowdin-body-color,rgba(38,50,56,0.87))]", children: row.value })] }, row.label))) }) }) }));
2556
- }
2557
- function renderPermissionStatus(status) {
2558
- switch (status) {
2559
- case 'willBeGranted':
2560
- return (jsxRuntimeExports.jsx("span", { className: "inline-block cursor-default rounded-[15px] bg-[var(--crowdin-info)] px-3 py-1 text-center text-xs font-medium text-[var(--crowdin-white)]", children: "Will Be Granted" }));
2561
- case 'willBeRegistered':
2562
- return (jsxRuntimeExports.jsx("span", { className: "inline-block cursor-default rounded-[15px] bg-[var(--crowdin-info)] px-3 py-1 text-center text-xs font-medium text-[var(--crowdin-white)]", children: "Will Be Registered" }));
2563
- case 'actionRequired':
2564
- return (jsxRuntimeExports.jsx("span", { className: "inline-block cursor-default rounded-[15px] bg-[var(--crowdin-warning-bg)] px-3 py-1 text-center text-xs font-medium text-[var(--crowdin-warning)]", children: "Action Required" }));
2565
- case 'granted':
2566
- default:
2567
- return jsxRuntimeExports.jsx("crowdin-p", { children: EMPTY_PERMISSION_VALUE });
2568
- }
2569
- }
2570
- function renderPermissionUsers(row) {
2571
- if (!row || row.users.length === 0) {
2572
- return row ? renderPermissionStatus(row.status) : null;
2573
- }
2574
- return (jsxRuntimeExports.jsx("ul", { className: "m-0 list-none p-0", children: row.users.map((user) => (jsxRuntimeExports.jsx("li", { className: "mb-1 text-sm last:mb-0", children: jsxRuntimeExports.jsx("crowdin-p", { children: user }) }, user))) }));
2575
- }
2576
- function renderPermissionRowDescription(row) {
2577
- if (!(row === null || row === void 0 ? void 0 : row.description)) {
2578
- return null;
2579
- }
2580
- return (jsxRuntimeExports.jsx("div", { className: row.isWarning
2581
- ? 'mt-1 text-xs text-[var(--crowdin-warning)]'
2582
- : 'mt-1 text-xs text-[var(--crowdin-text-muted)]', children: row.description }));
2583
- }
2584
- function openModal(modal) {
2585
- if (!modal) {
2586
- return;
2587
- }
2588
- modal.style.display = 'block';
2589
- modal.open();
2590
- }
2591
- function closeModal(modal) {
2592
- if (!modal) {
2593
- return;
2695
+ catchRejection(e, "Can't fetch error logs");
2594
2696
  }
2595
- modal.style.display = 'none';
2596
- modal.close();
2597
2697
  }
2598
- function settingsFieldChange(eventOrElement) {
2599
- var _a, _b;
2600
- const el = (eventOrElement === null || eventOrElement === void 0 ? void 0 : eventOrElement.currentTarget) || (eventOrElement === null || eventOrElement === void 0 ? void 0 : eventOrElement.target) || eventOrElement;
2601
- (_a = el === null || el === void 0 ? void 0 : el.removeAttribute) === null || _a === void 0 ? void 0 : _a.call(el, 'error');
2602
- (_b = el === null || el === void 0 ? void 0 : el.removeAttribute) === null || _b === void 0 ? void 0 : _b.call(el, 'error-text');
2698
+ function showIntegration() {
2699
+ setIsErrorLogsVisible(false);
2603
2700
  }
2604
- function isFolder(e) {
2605
- return !e.type && (e.nodeType === undefined || e.nodeType === folderType || e.nodeType === branchType);
2606
- }
2607
- return (jsxRuntimeExports.jsxs("div", { children: [jsxRuntimeExports.jsxs("div", { className: "mx-auto max-w-[1420px] px-6 py-4", children: [jsxRuntimeExports.jsxs("div", { className: "mb-2.5 text-right", children: [notice && (jsxRuntimeExports.jsxs("crowdin-alert", { id: "notice", title: notice.title, ...(!notice.icon ? { 'no-icon': 'true' } : {}), ...(notice.type ? { type: notice.type } : {}), style: { display: isNoticeVisible ? 'block' : 'none', marginBottom: '12px' }, children: [jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center", children: jsxRuntimeExports.jsx("p", { className: "m-0", dangerouslySetInnerHTML: { __html: notice.content } }) }), notice.close && (jsxRuntimeExports.jsx("crowdin-button", { onClick: () => closeAlert('notice'), class: "absolute top-0 right-0", icon: true, children: "close" }))] })), checkSubscription && (jsxRuntimeExports.jsx("crowdin-alert", { id: "subscription-info", "no-icon": "true", type: "warning", style: { display: subscriptionInfo ? 'block' : 'none', marginBottom: '12px' }, children: jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-center", children: [jsxRuntimeExports.jsx("p", { className: "m-0", children: subscriptionInfo ? `Your trial expires in ${subscriptionInfo.daysLeft} days.` : '' }), jsxRuntimeExports.jsx("crowdin-button", { class: "ml-2", primary: true, onClick: () => {
2608
- if (subscriptionInfo === null || subscriptionInfo === void 0 ? void 0 : subscriptionInfo.subscribeLink) {
2609
- window.open(subscriptionInfo.subscribeLink, '_blank', 'noopener,noreferrer');
2610
- }
2611
- }, children: "Subscribe" })] }) })), jsxRuntimeExports.jsxs("div", { id: "buttons", className: "flex w-full items-center justify-between", children: [jsxRuntimeExports.jsxs("div", { className: "mr-auto", children: [jsxRuntimeExports.jsx("span", { style: { display: isErrorLogsVisible ? undefined : 'none' }, children: jsxRuntimeExports.jsx("crowdin-button", { id: "show-integration-btn", "icon-before": "arrow_back", onClick: () => showIntegration(), children: "Integration" }) }), jsxRuntimeExports.jsx("span", { style: { display: isErrorLogsVisible ? 'none' : undefined }, children: jsxRuntimeExports.jsx("crowdin-button", { id: "show-error-logs-btn", "icon-before": "list", onClick: () => showErrorLogs(), children: "Error logs" }) })] }), isManager && (jsxRuntimeExports.jsx("crowdin-button", { "icon-before": "link", onClick: () => showPermissionsDialog(), children: "Share" })), infoModal && (jsxRuntimeExports.jsx("crowdin-button", { "icon-before": "info", onClick: () => openModal(infoModalRef.current), children: infoModal.title })), configurationFields && (jsxRuntimeExports.jsx("crowdin-button", { "icon-before": "settings", onClick: () => {
2612
- openModal(settingsModalRef.current);
2613
- fillSettingsForm();
2614
- }, children: "Settings" })), jsxRuntimeExports.jsx("crowdin-button", { ...(!isOwner ? { disabled: true, title: 'Only the owner can log out' } : {}), "icon-before": "account_circle", onClick: () => integrationLogout(), children: "Log out" })] })] }), jsxRuntimeExports.jsx("div", { style: { display: isErrorLogsVisible ? 'none' : undefined }, children: jsxRuntimeExports.jsx("crowdin-simple-integration", { ref: appComponent, "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)
2615
- ? { 'integration-schedule': 'true' }
2616
- : {}), ...(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", ...(((_a = integrationButtonMenuItems === null || integrationButtonMenuItems === void 0 ? void 0 : integrationButtonMenuItems.length) !== null && _a !== void 0 ? _a : 0) > 0
2617
- ? { 'integration-button-menu-items': integrationButtonMenuItems }
2618
- : {}), ...(((_b = crowdinButtonMenuItems === null || crowdinButtonMenuItems === void 0 ? void 0 : crowdinButtonMenuItems.length) !== null && _b !== void 0 ? _b : 0) > 0
2619
- ? { 'crowdin-button-menu-items': crowdinButtonMenuItems }
2620
- : {}), ...((filtering === null || filtering === void 0 ? void 0 : filtering.crowdinLanguages) ? { 'crowdin-filter': true } : {}), ...((filtering === null || filtering === void 0 ? void 0 : filtering.integrationFilterConfig)
2621
- ? {
2622
- 'integration-filter': true,
2623
- 'integration-filter-config': filtering.integrationFilterConfig,
2624
- }
2625
- : {}) }) }), jsxRuntimeExports.jsxs("div", { id: "user-errors", style: { display: isErrorLogsVisible ? undefined : 'none' }, children: [jsxRuntimeExports.jsx("crowdin-show-as-table", { ref: userErrorsTableRef, "is-loading": true, id: "user-errors-table", "is-searchable": true, "total-records": "25", "search-placeholder": "Search something" }), jsxRuntimeExports.jsx("crowdin-alert", { children: "This table displays the most recent error logs from the past month. Logs older than one month will be automatically deleted." })] })] }), jsxRuntimeExports.jsx("crowdin-toasts", {}), jsxRuntimeExports.jsx("crowdin-async-progress", { ref: asyncProgressRef, cancelAsyncAction: "" }), jsxRuntimeExports.jsxs("crowdin-modal", { ref: subscriptionModal, style: { display: 'none' }, id: "subscription-modal", "modal-width": "50", "close-button": "false", children: [jsxRuntimeExports.jsx("div", { children: jsxRuntimeExports.jsxs("crowdin-alert", { type: "warning", children: ["Subscribe to continue using the ", name, " app."] }) }), jsxRuntimeExports.jsxs("div", { slot: "footer", children: [jsxRuntimeExports.jsx("crowdin-button", { primary: true, onClick: () => window.open(subscriptionLink.current, '_blank', 'noopener,noreferrer'), children: "Subscribe" }), jsxRuntimeExports.jsx("crowdin-button", { outlined: true, onClick: () => window.open('https://crowdin.com/contacts', '_blank', 'noopener,noreferrer'), children: "Contact us" }), jsxRuntimeExports.jsx("crowdin-button", { class: "ml-2.5", secondary: true, onClick: () => integrationLogout(), children: "Log out" })] })] }), jsxRuntimeExports.jsxs("crowdin-modal", { ref: permissionsModalRef, style: { display: 'none' }, id: "permissions-modal", "modal-title": "Share", "close-button-title": "Close", "close-button": true, "body-overflow-unset": true, children: [jsxRuntimeExports.jsx("div", { className: modalLoaders['permissions-modal']
2626
- ? 'absolute inset-0 z-[4] flex items-center justify-center bg-white/30'
2627
- : 'hidden', children: jsxRuntimeExports.jsx("crowdin-progress-indicator", {}) }), jsxRuntimeExports.jsxs("div", { children: [jsxRuntimeExports.jsx("div", { children: jsxRuntimeExports.jsx("crowdin-users-select", { ref: usersSelectRef, "allow-new-options": true, "is-multi": true, "is-searchable": true, id: "users", label: "Users", "help-text": "Search for members by name, username, email, or invite new ones using their email.", "is-position-fixed": true, children: permissionUserOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.id, children: option.label }, option.id))) }, "users") }), jsxRuntimeExports.jsxs("div", { children: [jsxRuntimeExports.jsx("div", { className: isPermissionsConfirmVisible ? 'mt-4' : 'hidden', children: jsxRuntimeExports.jsxs("table", { className: "w-full border-separate border-spacing-0", children: [jsxRuntimeExports.jsx("thead", { children: jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsx("th", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left text-sm", style: { width: '40%' }, children: "Access" }), jsxRuntimeExports.jsx("th", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left text-sm", style: { width: '35%' }, children: "Users" }), jsxRuntimeExports.jsx("th", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-right text-sm", style: { width: '20%' }, children: "Upcoming changes" })] }) }), jsxRuntimeExports.jsxs("tbody", { children: [hasOrganization && (jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsxs("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: [jsxRuntimeExports.jsx("crowdin-p", { children: "Registration in Organization" }), renderPermissionRowDescription(permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.organization)] }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: renderPermissionUsers(permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.organization) }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-right align-top", children: (permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.organization) &&
2628
- renderPermissionStatus(permissionsSummary.organization.status) })] })), jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: jsxRuntimeExports.jsx("crowdin-p", { children: "Project Manager Access" }) }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: renderPermissionUsers(permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.project) }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-right align-top", children: (permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.project) &&
2629
- renderPermissionStatus(permissionsSummary.project.status) })] }), jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsxs("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: [jsxRuntimeExports.jsx("crowdin-p", { children: "Application Visibility Access" }), renderPermissionRowDescription(permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.applicationSettings)] }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: renderPermissionUsers(permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.applicationSettings) }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-right align-top", children: (permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.applicationSettings) &&
2630
- renderPermissionStatus(permissionsSummary.applicationSettings.status) })] }), jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsxs("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: [jsxRuntimeExports.jsx("crowdin-p", { children: "Access to the Integration" }), jsxRuntimeExports.jsx("div", { className: "mt-1 text-xs text-[var(--crowdin-text-muted)]", children: "This provides access to sync files and modify the current integration settings." })] }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-left align-top", children: renderPermissionUsers(permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.applicationCredentials) }), jsxRuntimeExports.jsx("td", { className: "border-b border-[var(--crowdin-border-color)] p-4 text-right align-top", children: (permissionsSummary === null || permissionsSummary === void 0 ? void 0 : permissionsSummary.applicationCredentials) &&
2631
- renderPermissionStatus(permissionsSummary.applicationCredentials.status) })] })] })] }) }), zenModeUrl && (jsxRuntimeExports.jsx("div", { className: "mt-4", children: jsxRuntimeExports.jsx("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 }) }))] })] }), jsxRuntimeExports.jsx("div", { slot: "footer", children: jsxRuntimeExports.jsx("crowdin-button", { id: "confirm-users-btn", outlined: true, onClick: () => inviteUsers(), children: "Save" }) })] }), excludedTargetLanguages && (jsxRuntimeExports.jsx("crowdin-modal", { ref: languageModal, style: { display: 'none' }, "modal-width": "50", "modal-title": "Select Target Languages for Translation", id: "excluded-languages", children: languageModalState && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("div", { children: jsxRuntimeExports.jsx("crowdin-select", { ref: languageSelectRef, "is-multi": true, "is-searchable": true, "is-position-fixed": true, "close-on-select": "false", name: "languages", id: "language-select", label: "Languages", children: languageModalState.languages.map((language) => (jsxRuntimeExports.jsx("option", { value: language.id, children: language.name }, language.id))) }) }), jsxRuntimeExports.jsxs("div", { slot: "footer", children: [jsxRuntimeExports.jsx("span", { hidden: languageModalState.action !== 'updateCrowdinTargetLanguages', children: jsxRuntimeExports.jsx("crowdin-button", { id: "update-crowdin-target-languages", outlined: true, onClick: () => updateCrowdinFilesTargetLanguages(), children: "Update Target Languages" }) }), jsxRuntimeExports.jsx("span", { hidden: languageModalState.action !== 'syncToCrowdin', children: jsxRuntimeExports.jsx("crowdin-button", { id: "upload-exclude-languages", outlined: true, onClick: () => uploadFilesToCrowdinWithExcludedLanguages(), children: jsxRuntimeExports.jsxs("div", { className: "inline-flex items-center gap-1", children: [jsxRuntimeExports.jsx("span", { children: "Sync to" }), jsxRuntimeExports.jsx("crowdin-logo", { "is-icon": true })] }) }) })] })] })) })), infoModal && (jsxRuntimeExports.jsx("crowdin-modal", { ref: infoModalRef, style: { display: 'none' }, id: "info-modal", "modal-width": "50", "modal-title": infoModal.title, "close-button-title": "Close", children: jsxRuntimeExports.jsx("div", { dangerouslySetInnerHTML: { __html: infoModal.content } }) })), configurationFields && (jsxRuntimeExports.jsxs("crowdin-modal", { ref: settingsModalRef, style: { display: 'none' }, id: "settings-modal", "modal-width": "65", "modal-title": "Settings", "close-button-title": "Close", children: [jsxRuntimeExports.jsx("div", { className: modalLoaders['settings-modal']
2632
- ? 'absolute inset-0 z-[4] flex items-center justify-center bg-white/30'
2633
- : 'hidden', children: jsxRuntimeExports.jsx("crowdin-progress-indicator", {}) }), jsxRuntimeExports.jsx("div", { id: "modal-content", children: jsxRuntimeExports.jsx("crowdin-tabs", { "tabs-config": JSON.stringify(configurationFields.map((f) => f.name)), "active-tab": "1", children: configurationFields.map((tab, tabIndex) => (jsxRuntimeExports.jsx("div", { slot: `tab-${tabIndex + 1}`, children: jsxRuntimeExports.jsx("div", { children: tab.fields.map((field, fieldIndex) => (jsxRuntimeExports.jsxs(reactExports.Fragment, { children: [renderConfigField(field, settingsFieldChange, registerSettingsField), jsxRuntimeExports.jsx("div", { style: { padding: '8px' }, ...(field.dependencySettings
2634
- ? { 'data-dependency': field.dependencySettings }
2635
- : {}) })] }, field.key || `${tabIndex}-${fieldIndex}`))) }) }, tabIndex))) }) }), jsxRuntimeExports.jsx("div", { slot: "footer", children: jsxRuntimeExports.jsx("crowdin-button", { id: "settings-save-btn", outlined: true, disabled: isSettingsSaveDisabled, onClick: () => saveSettings(), children: "Save" }) })] })), ((syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.crowdin) || (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration)) && (jsxRuntimeExports.jsxs("crowdin-modal", { ref: scheduleModalRef, 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, children: [jsxRuntimeExports.jsx("crowdin-checkbox", { ref: selectedFilesCheckboxRef, id: "selected-files", name: "selected-files", label: "Selected files", class: "hydrated", checked: scheduleModalState.selectedFiles, value: String(scheduleModalState.selectedFiles), onChange: () => onChangeAutoSynchronizationOptions() }), jsxRuntimeExports.jsx("crowdin-checkbox", { ref: allFolderFilesCheckboxRef, id: "all-folder-files", name: "all-folder-files", label: "All files in the folder", class: "hydrated", hidden: !scheduleModalState.showAllFolderFiles, checked: scheduleModalState.allFolderFiles, value: String(scheduleModalState.allFolderFiles), onChange: () => onChangeAutoSynchronizationOptions() }), jsxRuntimeExports.jsx("crowdin-checkbox", { ref: newFilesCheckboxRef, id: "new-files", name: "new-files", label: "New files", class: "hydrated", hidden: !scheduleModalState.showNewFiles, checked: scheduleModalState.newFiles, value: String(scheduleModalState.newFiles), ...(scheduleModalState.newFilesClearSelection ? { 'clear-selection': '' } : {}), onChange: () => onChangeAutoSynchronizationOptions() }), jsxRuntimeExports.jsx("div", { slot: "footer", children: jsxRuntimeExports.jsx("crowdin-button", { outlined: true, id: "save-schedule-sync", disabled: isScheduleSaveDisabled, onClick: () => saveScheduleSync(), children: "Save" }) })] })), jsxRuntimeExports.jsx("crowdin-modal", { ref: userErrorDetailModalRef, style: { display: 'none' }, id: "user-error-detail", "close-button-title": "Close", "close-button": "true", children: selectedUserError && getUserErrorDetail(selectedUserError) })] }));
2701
+ return (jsxRuntimeExports.jsxs("div", { children: [jsxRuntimeExports.jsxs("div", { className: "mx-auto max-w-[1420px] px-6 py-4", children: [jsxRuntimeExports.jsx(TopBar, { notice: notice, isNoticeVisible: isNoticeVisible, checkSubscription: checkSubscription, subscriptionInfo: subscriptionInfo, isErrorLogsVisible: isErrorLogsVisible, isManager: isManager, infoModal: infoModal, hasSettings: !!configurationFields, isOwner: isOwner, onCloseNotice: closeAlert, onShowIntegration: showIntegration, onShowErrorLogs: showErrorLogs, onShowPermissions: permissions.showPermissionsDialog, onOpenInfoModal: () => openModal(infoModalRef.current), onOpenSettings: () => {
2702
+ openModal(settingsModalRef.current);
2703
+ settings.fillSettingsForm();
2704
+ }, onLogout: integrationLogout }), jsxRuntimeExports.jsx(IntegrationView, { appComponentRef: appComponent, userErrorsTableRef: userErrorsTableRef, name: name, isErrorLogsVisible: isErrorLogsVisible, syncNewElements: syncNewElements, withCronSync: withCronSync, webhooks: webhooks, integrationOneLevelFetching: integrationOneLevelFetching, integrationSearchListener: integrationSearchListener, integrationPagination: integrationPagination, filtering: filtering, integrationButtonMenuItems: integrationButtonMenuItems, crowdinButtonMenuItems: crowdinButtonMenuItems })] }), jsxRuntimeExports.jsx("crowdin-toasts", {}), jsxRuntimeExports.jsx("crowdin-async-progress", { ref: asyncProgressRef, cancelAsyncAction: "" }), jsxRuntimeExports.jsx(SubscriptionModal, { modalRef: subscriptionModal, subscriptionLink: subscriptionLink, name: name, onLogout: integrationLogout }), jsxRuntimeExports.jsx(PermissionsModal, { modalRef: permissionsModalRef, usersSelectRef: permissions.usersSelectRef, isLoading: permissions.isLoading, permissionUserOptions: permissions.permissionUserOptions, isConfirmVisible: permissions.isPermissionsConfirmVisible, permissionsSummary: permissions.permissionsSummary, hasOrganization: hasOrganization, zenModeUrl: zenModeUrl, onSave: permissions.inviteUsers }), excludedTargetLanguages && (jsxRuntimeExports.jsx(ExcludedLanguagesModal, { modalRef: syncOps.languageModalRef, languageSelectRef: syncOps.languageSelectRef, languageModalState: syncOps.languageModalState, onUpdateTargetLanguages: syncOps.updateCrowdinFilesTargetLanguages, onSyncToCrowdin: syncOps.uploadFilesToCrowdinWithExcludedLanguages })), infoModal && jsxRuntimeExports.jsx(InfoModal, { modalRef: infoModalRef, infoModal: infoModal }), configurationFields && (jsxRuntimeExports.jsx(SettingsModal, { modalRef: settingsModalRef, isLoading: settings.isLoading, configurationFields: configurationFields, isSaveDisabled: settings.isSettingsSaveDisabled, settingsFieldChange: settings.settingsFieldChange, registerSettingsField: settings.registerSettingsField, onSave: settings.saveSettings })), ((syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.crowdin) || (syncNewElements === null || syncNewElements === void 0 ? void 0 : syncNewElements.integration)) && (jsxRuntimeExports.jsx(ScheduleModal, { modalRef: scheduleModalRef, selectedFilesCheckboxRef: schedule.selectedFilesCheckboxRef, allFolderFilesCheckboxRef: schedule.allFolderFilesCheckboxRef, newFilesCheckboxRef: schedule.newFilesCheckboxRef, scheduleModalState: schedule.scheduleModalState, isSaveDisabled: schedule.isScheduleSaveDisabled, onChangeOptions: schedule.onChangeAutoSynchronizationOptions, onSave: schedule.saveScheduleSync })), jsxRuntimeExports.jsx(UserErrorDetailModal, { modalRef: userErrorDetailModalRef, selectedUserError: selectedUserError })] }));
2636
2705
  };
2637
2706
 
2638
2707
  initializeCrowdinIframeContext();