@crowdin/app-project-module 0.71.2 → 0.73.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.
@@ -36,6 +36,7 @@
36
36
  <div id="buttons">
37
37
  <crowdin-button id="show-integration-btn" class="hidden" icon-before="arrow_back" onclick="showIntegration();">Integration</crowdin-button>
38
38
  <crowdin-button id="show-error-logs-btn" icon-before="list" onclick="showErrorLogs();">Error logs</crowdin-button>
39
+ <crowdin-button icon-before="manage_accounts" onclick="showPermissionsDialog()">Permissions</crowdin-button>
39
40
  {{#if infoModal}}
40
41
  <crowdin-button icon-before="info" onclick="openModal(infoModal);">{{infoModal.title}}</crowdin-button>
41
42
  {{/if}}
@@ -115,19 +116,113 @@
115
116
  <crowdin-button class="ml-10" secondary onclick="integrationLogout()">Log out</crowdin-button>
116
117
  </div>
117
118
  </crowdin-modal>
118
- {{#if infoModal}}
119
+
119
120
  <crowdin-modal
120
121
  style="display: none;"
121
- id="info-modal"
122
- modal-width="50"
123
- modal-title="{{infoModal.title}}"
122
+ id="permissions-modal"
123
+ modal-title="Permissions"
124
124
  close-button-title="Close"
125
+ close-button
126
+ body-overflow-unset
125
127
  >
126
- <div>
127
- {{{infoModal.content}}}
128
- </div>
128
+ <div class="loader hidden">
129
+ <crowdin-progress-indicator></crowdin-progress-indicator>
130
+ </div>
131
+ <div class="permissions-modal-content">
132
+ <div class="select-users-block">
133
+ <crowdin-users-select
134
+ allow-new-options
135
+ is-multi
136
+ is-searchable
137
+ id="users"
138
+ key="users"
139
+ label="Users"
140
+ help-text="Search for members by name, username, email, or invite new ones using their email."
141
+ is-position-fixed
142
+ onchange="inviteUsers()"
143
+ >
144
+ </crowdin-select>
145
+ </div>
146
+ <div class="select-users-info">
147
+ <div class="confirm-users-block mt-2 hidden">
148
+ <table>
149
+ <thead>
150
+ <tr>
151
+ <th style="width: 40%;">Permission Type (Upcoming changes)</th>
152
+ <th style="width: 35%;">Users</th>
153
+ <th class="status" style="width: 20%;">Status</th>
154
+ </tr>
155
+ </thead>
156
+ <tbody>
157
+ {{#if hasOrganization}}
158
+ <tr class="organization-invite">
159
+ <td>
160
+ <crowdin-p>Registration in Organization</crowdin-p>
161
+ </td>
162
+ <td class="affected-users"></td>
163
+ <td class="status"></td>
164
+ </tr>
165
+ {{/if}}
166
+ <tr class="project-invite">
167
+ <td>
168
+ <crowdin-p>Project Manager Access</crowdin-p>
169
+ </td>
170
+ <td class="affected-users"></td>
171
+ <td class="status"></td>
172
+ </tr>
173
+ <tr class="application-settings-invite">
174
+ <td>
175
+ <crowdin-p>Application Access</crowdin-p>
176
+ <div class="permission-description">{{name}} installation settings will be updated.</div>
177
+ </td>
178
+ <td class="affected-users"></td>
179
+ <td class="status"></td>
180
+ </tr>
181
+ <tr class="application-credentials-invite">
182
+ <td>
183
+ <crowdin-p>Access to the Integration</crowdin-p>
184
+ <div class="permission-description">{{name}} settings will be updated.</div>
185
+ </td>
186
+ <td class="affected-users"></td>
187
+ <td class="status"></td>
188
+ </tr>
189
+ </tbody>
190
+ </table>
191
+ </div>
192
+ {{#if zenModeUrl}}
193
+ <div class="mt-2">
194
+ <crowdin-input
195
+ with-fixed-height
196
+ label="Zen Mode link"
197
+ help-text="This focused view allows you to concentrate solely on the Integrations section, eliminating distractions and providing a streamlined experience for managing your project integrations."
198
+ value="{{zenModeUrl}}"
199
+ name="zenModeLink"
200
+ with-copy-button
201
+ ></crowdin-input>
202
+ </div>
203
+ {{/if}}
204
+ </div>
205
+ </div>
206
+
207
+ <div slot="footer">
208
+ <crowdin-button id="confirm-users-btn" outlined onclick="inviteUsers(false)">Save</crowdin-button>
209
+ </div>
129
210
  </crowdin-modal>
211
+
212
+ {{#if infoModal}}
213
+ <crowdin-modal
214
+ style="display: none;"
215
+ id="info-modal"
216
+ modal-width="50"
217
+ modal-title="{{infoModal.title}}"
218
+ close-button-title="Close"
219
+ >
220
+ <div>
221
+ {{{infoModal.content}}}
222
+ </div>
223
+ </crowdin-modal>
130
224
  {{/if}}
225
+
131
226
  {{#if configurationFields}}
132
227
  <crowdin-modal
133
228
  style="display: none;"
@@ -785,7 +880,7 @@
785
880
  }
786
881
 
787
882
  function saveSettings() {
788
- setLoader();
883
+ setLoader('#settings-modal');
789
884
  const settingsElements = Array.from(document.getElementById('modal-content').children);
790
885
  const tags = ['crowdin-checkbox', 'crowdin-select', 'crowdin-input'];
791
886
  const configReq = {};
@@ -820,7 +915,7 @@
820
915
  })
821
916
  .catch(e => catchRejection(e, 'Can\'t save settings'))
822
917
  .finally(() => {
823
- unsetLoader();
918
+ unsetLoader('#settings-modal');
824
919
  settingsSaveBtn.removeAttribute('disabled');
825
920
  closeModal(settingsModal);
826
921
  {{#if reloadOnConfigSave}}
@@ -830,13 +925,174 @@
830
925
  });
831
926
  }
832
927
 
833
- function setLoader() {
834
- const loader = document.querySelector('#settings-modal .loader');
928
+ function showConfirmUsersBlock() {
929
+ const confirmUsersBlock = document.querySelector('.confirm-users-block');
930
+ confirmUsersBlock.classList.remove('hidden');
931
+
932
+ const permissionsModal = document.getElementById('permissions-modal');
933
+ permissionsModal.removeAttribute('body-overflow-unset')
934
+ }
935
+
936
+ function hideConfirmUsersBlock() {
937
+ const confirmUsersBlock = document.querySelector('.confirm-users-block');
938
+ confirmUsersBlock.classList.add('hidden');
939
+
940
+ const permissionsModal = document.getElementById('permissions-modal');
941
+ permissionsModal.setAttribute('body-overflow-unset', true)
942
+ }
943
+
944
+ function showPermissionsDialog() {
945
+ hideConfirmUsersBlock();
946
+ openModal(permissions)
947
+ setLoader('#permissions-modal');
948
+ const select = document.getElementById('users');
949
+
950
+ select.value = '[]';
951
+
952
+ checkOrigin()
953
+ .then(restParams => fetch('api/users' + restParams))
954
+ .then(checkResponse)
955
+ .then((res) => {
956
+ console.log(res)
957
+ let userOptions = res.data.users.map(user => `<option value="${user.id}">${user.name}</option>`).join('');
958
+ select.innerHTML = userOptions;
959
+ select.value = JSON.stringify(res.data.managers);
960
+ })
961
+ .catch(e => catchRejection(e, 'Can\'t fetch users'))
962
+ .finally(() => unsetLoader('#permissions-modal'));
963
+ }
964
+
965
+ async function inviteUsers(onlyCheck = true) {
966
+ setLoader('#permissions-modal');
967
+
968
+ const select = document.getElementById('users');
969
+
970
+ if (onlyCheck && select.value === '[]') {
971
+ hideConfirmUsersBlock();
972
+ unsetLoader('#permissions-modal');
973
+ return;
974
+ }
975
+
976
+ const params = {
977
+ users: JSON.parse(select.value),
978
+ onlyCheck,
979
+ };
980
+
981
+ checkOrigin()
982
+ .then(restParams => fetch('api/invite-users' + restParams, {
983
+ method: 'POST',
984
+ headers: { 'Content-Type': 'application/json' },
985
+ body: JSON.stringify(params)
986
+ }))
987
+ .then(checkResponse)
988
+ .then((response) => {
989
+ if (!onlyCheck) {
990
+ showToast('Users successfully updated');
991
+ hideConfirmUsersBlock();
992
+ closeModal(permissions);
993
+ return;
994
+ }
995
+
996
+ prepareUsersConfirmBlock(response.data);
997
+ })
998
+ .catch(e => {
999
+ catchRejection(e, e?.error || 'Can\'t invite users')
1000
+ })
1001
+ .finally(() => unsetLoader('#permissions-modal'));
1002
+ }
1003
+
1004
+ function prepareUsersConfirmBlock(usersData) {
1005
+ showConfirmUsersBlock();
1006
+
1007
+ const organizationInvite = document.querySelector('.organization-invite');
1008
+ const projectInvite = document.querySelector('.project-invite');
1009
+ const applicationCredentialsInvite = document.querySelector('.application-credentials-invite');
1010
+
1011
+ const grantedElement = '<span class="badge badge-granted">Available</span>';
1012
+ const willGrantedElement = '<span class="badge badge-will-be-granted">Will Be Granted</span>';
1013
+ const notAvailableElement = '<span class="badge badge-not-available">Manual Action Required</span>';
1014
+
1015
+ // only in enterprise
1016
+ if (organizationInvite) {
1017
+ const organizationWillGrantElement = '<span class="badge badge-will-be-granted">Will be registered</span>';
1018
+
1019
+ processUsersWhoWillBeInvited(organizationInvite, usersData.usersWhoWillBeInvitedToOrganization, organizationWillGrantElement, grantedElement);
1020
+ }
1021
+
1022
+ processUsersWhoWillBeInvited(projectInvite, usersData.usersWhoWillBeInvitedToProject, willGrantedElement, grantedElement);
1023
+ processUsersWhoWillBeInvited(applicationCredentialsInvite, usersData.usersWhoNotIssetInIntegration, willGrantedElement, grantedElement);
1024
+
1025
+ processUsersWhoWillBeInvitedApplicationSettings(usersData, willGrantedElement, grantedElement, notAvailableElement);
1026
+ }
1027
+
1028
+ function processUsersWhoWillBeInvited(element, users, grantMessage, alreadyGrantedMessage) {
1029
+ const tooltip = element.querySelector('.status');
1030
+ const userList = element.querySelector('.affected-users');
1031
+
1032
+ if (users.length) {
1033
+ tooltip.innerHTML = grantMessage;
1034
+
1035
+ let affectedUsers = '<ul>';
1036
+ for (const user of users) {
1037
+ affectedUsers += `<li><crowdin-p>${user.name}</crowdin-p></li>`;
1038
+ }
1039
+ affectedUsers += '</ul>';
1040
+ userList.innerHTML = affectedUsers;
1041
+ } else {
1042
+ tooltip.innerHTML = alreadyGrantedMessage;
1043
+ userList.innerHTML = '<crowdin-p>&horbar;</crowdin-p>';
1044
+ }
1045
+ }
1046
+
1047
+ function processUsersWhoWillBeInvitedApplicationSettings(usersData, willGrantedElement, grantedElement, notAvailableElement) {
1048
+ const applicationSettingsInvite = document.querySelector('.application-settings-invite');
1049
+
1050
+ const tooltip = applicationSettingsInvite.querySelector('.status');
1051
+ const userList = applicationSettingsInvite.querySelector('.affected-users');
1052
+
1053
+ let affectedUsers = '<ul>';
1054
+
1055
+ if (!usersData.editApplicationAvailable) {
1056
+ tooltip.innerHTML = `<crowdin-tooltip
1057
+ position="left"
1058
+ tooltip=">The application doesn't have permission to update this setting.">
1059
+ ${notAvailableElement}
1060
+ </crowdin-tooltip>`;
1061
+ for (const user of usersData.usersWhoNotIssetInApplicationInstallation) {
1062
+ affectedUsers += `<li><crowdin-p>${user.name}</crowdin-p></li>`;
1063
+ }
1064
+ userList.innerHTML = affectedUsers + '</ul>';
1065
+ } else if (!usersData.usersWhoNotIssetInApplicationInstallation.length) {
1066
+ tooltip.innerHTML = grantedElement;
1067
+ userList.innerHTML = '<crowdin-p>&horbar;</crowdin-p>';
1068
+ } else {
1069
+ if (usersData.isAdmin) {
1070
+ tooltip.innerHTML = willGrantedElement;
1071
+ for (const user of usersData.usersWhoNotIssetInApplicationInstallation) {
1072
+ affectedUsers += `<li><crowdin-p>${user.name}</crowdin-p></li>`;
1073
+ }
1074
+ userList.innerHTML = affectedUsers + '</ul>';
1075
+ } else {
1076
+ tooltip.innerHTML = `<crowdin-tooltip
1077
+ position="left"
1078
+ tooltip="Only organization admins have permission to update this setting.">
1079
+ ${notAvailableElement}
1080
+ </crowdin-tooltip>`;
1081
+ for (const user of usersData.usersWhoNotIssetInApplicationInstallation) {
1082
+ affectedUsers += `<li><crowdin-p>${user.name}</crowdin-p></li>`;
1083
+ }
1084
+ userList.innerHTML = affectedUsers + '</ul>';
1085
+ }
1086
+ }
1087
+ }
1088
+
1089
+ function setLoader(id) {
1090
+ const loader = document.querySelector(`${id} .loader`);
835
1091
  loader.classList.remove('hidden');
836
1092
  }
837
1093
 
838
- function unsetLoader() {
839
- const loader = document.querySelector('#settings-modal .loader');
1094
+ function unsetLoader(id) {
1095
+ const loader = document.querySelector(`${id} .loader`);
840
1096
  setTimeout(function() {
841
1097
  loader.classList.add('hidden');
842
1098
  }, 500)
@@ -845,6 +1101,8 @@
845
1101
  const settingsModal = undefined;
846
1102
  {{/if}}
847
1103
 
1104
+ const permissions = document.getElementById('permissions-modal');
1105
+
848
1106
  {{#if infoModal}}
849
1107
  const infoModal = document.getElementById('info-modal');
850
1108
  {{else}}
@@ -853,7 +1111,9 @@
853
1111
 
854
1112
  document.addEventListener('keydown', (event) => {
855
1113
  if (event.keyCode == 27) {
856
-
1114
+ if (users) {
1115
+ closeModal(permissions);
1116
+ }
857
1117
  if (infoModal) {
858
1118
  closeModal(infoModal);
859
1119
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.71.2",
3
+ "version": "0.73.0",
4
4
  "description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
5
5
  "main": "out/index.js",
6
6
  "types": "out/index.d.ts",