@underpostnet/underpost 2.97.1 → 2.97.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +2 -2
  2. package/cli.md +3 -1
  3. package/conf.js +2 -0
  4. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  5. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  6. package/package.json +1 -1
  7. package/src/api/core/core.service.js +0 -5
  8. package/src/api/default/default.service.js +7 -5
  9. package/src/api/document/document.model.js +1 -1
  10. package/src/api/document/document.router.js +5 -0
  11. package/src/api/document/document.service.js +105 -47
  12. package/src/api/file/file.model.js +112 -4
  13. package/src/api/file/file.ref.json +42 -0
  14. package/src/api/file/file.service.js +380 -32
  15. package/src/api/user/user.model.js +38 -1
  16. package/src/api/user/user.router.js +96 -63
  17. package/src/api/user/user.service.js +81 -48
  18. package/src/cli/db.js +424 -166
  19. package/src/cli/index.js +8 -0
  20. package/src/cli/repository.js +1 -1
  21. package/src/cli/run.js +1 -0
  22. package/src/cli/ssh.js +10 -10
  23. package/src/client/components/core/Account.js +327 -36
  24. package/src/client/components/core/AgGrid.js +3 -0
  25. package/src/client/components/core/Auth.js +9 -3
  26. package/src/client/components/core/Chat.js +2 -2
  27. package/src/client/components/core/Content.js +159 -78
  28. package/src/client/components/core/CssCore.js +16 -12
  29. package/src/client/components/core/FileExplorer.js +115 -8
  30. package/src/client/components/core/Input.js +204 -11
  31. package/src/client/components/core/LogIn.js +42 -20
  32. package/src/client/components/core/Modal.js +138 -24
  33. package/src/client/components/core/Panel.js +69 -31
  34. package/src/client/components/core/PanelForm.js +262 -77
  35. package/src/client/components/core/PublicProfile.js +888 -0
  36. package/src/client/components/core/Router.js +117 -15
  37. package/src/client/components/core/SearchBox.js +329 -13
  38. package/src/client/components/core/SignUp.js +26 -7
  39. package/src/client/components/core/SocketIo.js +6 -3
  40. package/src/client/components/core/Translate.js +98 -0
  41. package/src/client/components/core/Validator.js +15 -0
  42. package/src/client/components/core/windowGetDimensions.js +6 -6
  43. package/src/client/components/default/MenuDefault.js +59 -12
  44. package/src/client/components/default/RoutesDefault.js +1 -0
  45. package/src/client/services/core/core.service.js +163 -1
  46. package/src/client/services/default/default.management.js +451 -64
  47. package/src/client/services/default/default.service.js +13 -6
  48. package/src/client/services/file/file.service.js +43 -16
  49. package/src/client/services/user/user.service.js +13 -9
  50. package/src/db/DataBaseProvider.js +1 -1
  51. package/src/db/mongo/MongooseDB.js +1 -1
  52. package/src/index.js +1 -1
  53. package/src/mailer/MailerProvider.js +4 -4
  54. package/src/runtime/express/Express.js +2 -1
  55. package/src/runtime/lampp/Lampp.js +2 -2
  56. package/src/server/auth.js +3 -6
  57. package/src/server/data-query.js +449 -0
  58. package/src/server/object-layer.js +0 -3
  59. package/src/ws/IoInterface.js +2 -2
@@ -13,8 +13,8 @@ const Chat = {
13
13
  this.Data[idModal] = {};
14
14
  setTimeout(() => {
15
15
  Modal.Data[idModal].onObserverListener[`chat-${idModal}`] = (options) => {
16
- const { width, height } = options;
17
- s(`.chat-box`).style.height = `${height * 0.5}px`;
16
+ const { height } = options;
17
+ s(`.chat-box`).style.height = `${height - 250}px`;
18
18
  };
19
19
  s(`.btn-send-chat-${idModal}`).onclick = (e) => {
20
20
  e.preventDefault();
@@ -5,7 +5,7 @@ import { s4 } from './CommonJs.js';
5
5
  import { Translate } from './Translate.js';
6
6
  import { Modal, renderViewTitle } from './Modal.js';
7
7
  import { DocumentService } from '../../services/document/document.service.js';
8
- import { CoreService, getApiBaseUrl } from '../../services/core/core.service.js';
8
+ import { CoreService, getApiBaseUrl, headersFactory } from '../../services/core/core.service.js';
9
9
  import { loggerFactory } from './Logger.js';
10
10
  import { imageShimmer, renderChessPattern, renderCssAttr, styleFactory } from './Css.js';
11
11
  import { getQueryParams } from './Router.js';
@@ -39,20 +39,32 @@ const Content = {
39
39
  }
40
40
  documentObj = data[0];
41
41
  }
42
- if (documentObj.fileId) {
42
+
43
+ // Get file metadata (does not include buffer data)
44
+ if (documentObj.fileId && documentObj.fileId._id) {
43
45
  const { data, status, message } = await FileService.get({ id: documentObj.fileId._id });
44
46
  if (status !== 'success' || !data || !data[0]) {
45
- logger.error(message);
46
- // throw new Error(`no-preview-available`);
47
+ logger.warn('File metadata not found:', message);
48
+ // Continue without file - not fatal
49
+ } else {
50
+ file = data[0];
47
51
  }
48
- file = data[0];
49
52
  }
50
- if (documentObj.mdFileId) {
51
- const { data, status, message } = await FileService.get({ id: documentObj.mdFileId });
53
+
54
+ // Get markdown file metadata (optional)
55
+ if (documentObj.mdFileId && documentObj.mdFileId._id) {
56
+ const { data, status, message } = await FileService.get({ id: documentObj.mdFileId._id });
52
57
  if (status !== 'success' || !data || !data[0]) {
53
- logger.error(message);
54
- throw new Error(`no-preview-available`);
55
- } else md = data[0];
58
+ logger.warn('Markdown file metadata not found:', message);
59
+ // Continue without markdown - try to render file instead
60
+ } else {
61
+ md = data[0];
62
+ }
63
+ }
64
+
65
+ // Check if we have at least one file to render
66
+ if (!md && !file) {
67
+ throw new Error(`no-preview-available`);
56
68
  }
57
69
 
58
70
  htmls(
@@ -63,6 +75,8 @@ const Content = {
63
75
  })} `,
64
76
  );
65
77
  htmls(`.content-render-${idModal}`, ``);
78
+
79
+ // Pass file IDs to RenderFile - it will fetch blobs as needed
66
80
  if (md) await this.RenderFile({ idModal, file: md, id: md._id });
67
81
  if (file) await this.RenderFile({ idModal, file, id: file._id });
68
82
  Modal.Data[idModal].onObserverListener[`main-content-observer`]();
@@ -89,17 +103,48 @@ const Content = {
89
103
  </div>
90
104
  <div class="abs center error-${idModal} hide"></div>`;
91
105
  },
106
+
107
+ /**
108
+ * Helper function to get file content
109
+ * Supports both legacy format (with buffer data) and new format (metadata only)
110
+ * For new format, fetches content from blob endpoint
111
+ */
112
+ getFileContent: async function (file, options = {}) {
113
+ // If custom URL provided, use it
114
+ if (options.url) {
115
+ return await CoreService.getRaw({ url: options.url });
116
+ }
117
+
118
+ // If buffer data exists in file object (legacy format), use it
119
+ if (file.data?.data) {
120
+ return await getRawContentFile(getBlobFromUint8ArrayFile(file.data.data, file.mimetype));
121
+ }
122
+
123
+ // Otherwise, fetch from blob endpoint (new metadata-only format)
124
+ if (file._id) {
125
+ try {
126
+ const { data: blobArray, status } = await FileService.get({ id: `blob/${file._id}` });
127
+ if (status === 'success' && blobArray && blobArray[0]) {
128
+ return await blobArray[0].text();
129
+ }
130
+ throw new Error('Failed to fetch file content');
131
+ } catch (error) {
132
+ logger.error('Error fetching file content from blob endpoint:', error);
133
+ throw new Error('Could not load file content');
134
+ }
135
+ }
136
+
137
+ throw new Error('No file content available');
138
+ },
139
+
92
140
  RenderFile: async function (
93
141
  options = {
94
142
  file: {
95
143
  _id: '',
96
- data: {
97
- data: [0],
98
- },
99
144
  mimetype: '',
100
145
  url: '',
101
146
  name: '',
102
- cid: '', // TODO: IPFS env
147
+ cid: '',
103
148
  },
104
149
  idModal: '',
105
150
  style: {},
@@ -116,80 +161,116 @@ const Content = {
116
161
  border: 'none',
117
162
  };
118
163
  if (!options.class) options.class = ``;
164
+
119
165
  const { container, file } = options;
120
- const ext = file.name.split('.')[file.name.split('.').length - 1];
166
+ const ext = (file.name || '').split('.').pop()?.toLowerCase() || '';
121
167
  let render = '';
122
168
 
123
- switch (ext) {
124
- case 'md':
125
- {
126
- const content = options.url
127
- ? await CoreService.getRaw({ url: options.url })
128
- : await getRawContentFile(getBlobFromUint8ArrayFile(file.data.data, file.mimetype));
129
- render += html`<div class="${options.class}" ${styleFactory(options.style)}>${marked.parse(content)}</div>`;
169
+ try {
170
+ switch (ext) {
171
+ case 'md':
172
+ {
173
+ const content = await Content.getFileContent(file, options);
174
+ render += html`<div class="${options.class}" ${styleFactory(options.style)}>${marked.parse(content)}</div>`;
175
+ }
176
+ break;
177
+
178
+ case 'jpg':
179
+ case 'jpeg':
180
+ case 'webp':
181
+ case 'svg':
182
+ case 'gif':
183
+ case 'png': {
184
+ const url = await Content.urlFactory(options);
185
+ if (url) {
186
+ const imgRender = html`<img
187
+ alt="${file.name ? file.name : `file ${s4()}`}"
188
+ class="in ${options.class}"
189
+ ${styleFactory(options.style, `${renderChessPattern(50)}`)}
190
+ src="${url}"
191
+ />`;
192
+ render += imgRender;
193
+ } else {
194
+ render = html`<div class="in ${options.class}" ${styleFactory(options.style)}>
195
+ <p style="color: red;">Error loading image</p>
196
+ </div>`;
197
+ }
198
+ break;
130
199
  }
131
200
 
132
- break;
133
-
134
- case 'jpg':
135
- case 'jpeg':
136
- case 'webp':
137
- case 'svg':
138
- case 'gif':
139
- case 'png': {
140
- const url = Content.urlFactory(options);
141
- const imgRender = html`<img
142
- alt="${file.name ? file.name : `file ${s4()}`}"
143
- class="in ${options.class}"
144
- ${styleFactory(options.style, `${renderChessPattern(50)}`)}
145
- src="${url}"
146
- />`;
147
- render += imgRender;
148
- break;
149
- }
150
- case 'pdf': {
151
- const url = Content.urlFactory(options);
152
- render += html`<iframe
153
- class="in ${options.class} iframe-${options.idModal}"
154
- ${styleFactory(options.style)}
155
- src="${url}"
156
- ></iframe>`;
157
- break;
158
- }
201
+ case 'pdf': {
202
+ const url = await Content.urlFactory(options);
203
+ if (url) {
204
+ render += html`<iframe
205
+ class="in ${options.class} iframe-${options.idModal}"
206
+ ${styleFactory(options.style)}
207
+ src="${url}"
208
+ ></iframe>`;
209
+ } else {
210
+ render = html`<div class="in ${options.class}" ${styleFactory(options.style)}>
211
+ <p style="color: red;">Error loading PDF</p>
212
+ </div>`;
213
+ }
214
+ break;
215
+ }
216
+
217
+ case 'json':
218
+ {
219
+ const content = await Content.getFileContent(file, options);
220
+ render += html`<pre class="in ${options.class}" ${styleFactory(options.style)}>
221
+ ${JSON.stringify(JSON.parse(content), null, 4)}</pre
222
+ >`;
223
+ }
224
+ break;
159
225
 
160
- case 'json':
161
- render += html`<pre class="in ${options.class}" ${styleFactory(options.style)}>
162
- ${JSON.stringify(
163
- JSON.parse(
164
- options.url
165
- ? await CoreService.getRaw({ url: options.url })
166
- : await getRawContentFile(getBlobFromUint8ArrayFile(file.data.data, file.mimetype)),
167
- ),
168
- null,
169
- 4,
170
- )}</pre
171
- >`;
172
- break;
173
-
174
- default:
175
- render += html`<div class="in ${options.class}" ${styleFactory(options.style)}>
176
- ${options.url
177
- ? await CoreService.getRaw({ url: options.url })
178
- : await getRawContentFile(getBlobFromUint8ArrayFile(file.data.data, file.mimetype))}
179
- </div>`;
180
- break;
226
+ default:
227
+ {
228
+ const content = await Content.getFileContent(file, options);
229
+ render += html`<div class="in ${options.class}" ${styleFactory(options.style)}>${content}</div>`;
230
+ }
231
+ break;
232
+ }
233
+ } catch (error) {
234
+ logger.error('Error rendering file:', error);
235
+ render = html`<div class="in ${options.class}" ${styleFactory(options.style)}>
236
+ <p style="color: red;">Error loading file: ${error.message}</p>
237
+ </div>`;
181
238
  }
239
+
182
240
  if (options.raw) return render;
183
241
  append(container, render);
184
242
  },
185
- urlFactory: function (options) {
186
- return options.url
187
- ? options.url
188
- : options.file?.data?.data
189
- ? URL.createObjectURL(getBlobFromUint8ArrayFile(options.file.data.data, options.file.mimetype))
190
- : options.file._id
191
- ? getApiBaseUrl({ id: options.file._id, endpoint: 'file/blob' })
192
- : null;
243
+
244
+ /**
245
+ * Generate appropriate URL for file display
246
+ * Prefers blob endpoint for new metadata-only format
247
+ */
248
+ urlFactory: async function (options) {
249
+ // If custom URL provided, use it
250
+ if (options.url) {
251
+ return options.url;
252
+ }
253
+
254
+ // If buffer data exists (legacy), create object URL
255
+ if (options.file?.data?.data) {
256
+ return URL.createObjectURL(getBlobFromUint8ArrayFile(options.file.data.data, options.file.mimetype));
257
+ }
258
+
259
+ // Use blob endpoint for metadata-only format with proper authentication
260
+ if (options.file?._id) {
261
+ try {
262
+ const { data: blobArray, status } = await FileService.get({ id: `blob/${options.file._id}` });
263
+ if (status === 'success' && blobArray && blobArray[0]) {
264
+ return URL.createObjectURL(blobArray[0]);
265
+ }
266
+ throw new Error('Failed to fetch file blob');
267
+ } catch (error) {
268
+ logger.error('Error fetching file blob:', error);
269
+ return null;
270
+ }
271
+ }
272
+
273
+ return null;
193
274
  },
194
275
  };
195
276
 
@@ -232,6 +232,9 @@ const CssCommonCore = async () => {
232
232
  width: 40px;
233
233
  text-align: center;
234
234
  }
235
+ .input-container {
236
+ width: 275px;
237
+ }
235
238
  </style>
236
239
  ${boxShadow({ selector: '.account-profile-image' })}
237
240
  <div class="ag-grid-style"></div>`;
@@ -404,9 +407,7 @@ const CssCoreDark = {
404
407
  color: #fff;
405
408
  background: #313131;
406
409
  }
407
- .input-container {
408
- width: 256px;
409
- }
410
+
410
411
  .btn-eye-password {
411
412
  text-align: center;
412
413
  background: #1a1a1a;
@@ -480,7 +481,7 @@ const CssCoreDark = {
480
481
  cursor: pointer;
481
482
  }
482
483
  .btn-custom {
483
- width: 260px;
484
+ width: 278px;
484
485
  font-size: 20px;
485
486
  padding: 10px;
486
487
  min-height: 45px;
@@ -491,7 +492,7 @@ const CssCoreDark = {
491
492
  }
492
493
  .toggle-form-container,
493
494
  .dropdown-option {
494
- width: 238px;
495
+ width: 255px;
495
496
  font-size: 20px;
496
497
  padding: 10px;
497
498
  }
@@ -503,7 +504,7 @@ const CssCoreDark = {
503
504
  background: #232323;
504
505
  }
505
506
  .form-button {
506
- width: 260px;
507
+ width: 278px;
507
508
  font-size: 20px;
508
509
  padding: 10px;
509
510
  text-align: center;
@@ -599,10 +600,14 @@ const CssCoreLight = {
599
600
  }
600
601
 
601
602
  .box-shadow {
602
- box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
603
+ box-shadow:
604
+ 0 4px 8px 0 rgba(0, 0, 0, 0.2),
605
+ 0 6px 20px 0 rgba(0, 0, 0, 0.19);
603
606
  }
604
607
  .box-shadow:hover {
605
- box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2), 0 10px 30px 0 rgba(0, 0, 0, 0.3);
608
+ box-shadow:
609
+ 0 8px 16px 0 rgba(0, 0, 0, 0.2),
610
+ 0 10px 30px 0 rgba(0, 0, 0, 0.3);
606
611
  }
607
612
  .box-content-border {
608
613
  border: 2px solid #bbb;
@@ -715,7 +720,6 @@ const CssCoreLight = {
715
720
  border-radius: 5px;
716
721
  border: 2px solid #bbb;
717
722
  transition: 0.3s;
718
- width: 256px;
719
723
  }
720
724
  .input-container:hover {
721
725
  color: #1a1a1a;
@@ -793,7 +797,7 @@ const CssCoreLight = {
793
797
  cursor: pointer;
794
798
  }
795
799
  .btn-custom {
796
- width: 260px;
800
+ width: 278px;
797
801
  font-size: 20px;
798
802
  padding: 10px;
799
803
  min-height: 45px;
@@ -805,7 +809,7 @@ const CssCoreLight = {
805
809
  }
806
810
  .toggle-form-container,
807
811
  .dropdown-option {
808
- width: 238px;
812
+ width: 255px;
809
813
  font-size: 20px;
810
814
  padding: 10px;
811
815
  }
@@ -817,7 +821,7 @@ const CssCoreLight = {
817
821
  background: #e4e4e4;
818
822
  }
819
823
  .form-button {
820
- width: 260px;
824
+ width: 278px;
821
825
  font-size: 20px;
822
826
  padding: 10px;
823
827
  text-align: center;
@@ -156,6 +156,15 @@ const FileExplorer = {
156
156
 
157
157
  EventsUI.onClick(`.btn-input-file-explorer`, async (e) => {
158
158
  e.preventDefault();
159
+
160
+ // Check authentication before upload
161
+ if (!Auth.getToken()) {
162
+ return NotificationManager.Push({
163
+ html: Translate.Render(`error-user-not-authenticated`),
164
+ status: 'error',
165
+ });
166
+ }
167
+
159
168
  const { errorMessage } = await validators();
160
169
  if (!formBodyFiles)
161
170
  return NotificationManager.Push({
@@ -166,6 +175,12 @@ const FileExplorer = {
166
175
  let fileData;
167
176
  {
168
177
  const { status, data } = await FileService.post({ body: formBodyFiles });
178
+ if (status === 'error' || !data) {
179
+ return NotificationManager.Push({
180
+ html: Translate.Render(`error-upload-file`),
181
+ status: 'error',
182
+ });
183
+ }
169
184
  fileData = data;
170
185
  }
171
186
  {
@@ -259,6 +274,8 @@ const FileExplorer = {
259
274
  // params.data._id
260
275
 
261
276
  this.eGui = document.createElement('div');
277
+ const isPublic = params.data.isPublic;
278
+ const toggleId = `toggle-public-${params.data._id}`;
262
279
  this.eGui.innerHTML = html`
263
280
  <div class="fl">
264
281
  ${await BtnIcon.Render({
@@ -281,6 +298,13 @@ const FileExplorer = {
281
298
  label: html`<i class="fas fa-copy"></i>`,
282
299
  type: 'button',
283
300
  })}
301
+ ${await BtnIcon.Render({
302
+ class: `in fll management-table-btn-mini ${toggleId}`,
303
+ label: isPublic
304
+ ? html`<i class="fas fa-globe" style="color: #4caf50;"></i>`
305
+ : html`<i class="fas fa-lock" style="color: #9e9e9e;"></i>`,
306
+ type: 'button',
307
+ })}
284
308
  </div>
285
309
  `;
286
310
 
@@ -315,13 +339,21 @@ const FileExplorer = {
315
339
 
316
340
  EventsUI.onClick(`.btn-file-download-${params.data._id}`, async (e) => {
317
341
  e.preventDefault();
318
- console.log(params);
319
- const {
320
- data: [file],
321
- status,
322
- } = await FileService.get({ id: params.data.fileId });
323
-
324
- downloadFile(new Blob([new Uint8Array(file.data.data)], { type: params.data.mimetype }), params.data.name);
342
+ try {
343
+ // Use FileService with blob/ prefix for centralized blob fetching
344
+ const { data: blobArray, status } = await FileService.get({ id: `blob/${params.data.fileId}` });
345
+ if (status === 'success' && blobArray && blobArray[0]) {
346
+ downloadFile(blobArray[0], params.data.name);
347
+ } else {
348
+ throw new Error('Failed to fetch file blob');
349
+ }
350
+ } catch (error) {
351
+ logger.error('Download failed:', error);
352
+ NotificationManager.Push({
353
+ html: 'Download failed',
354
+ status: 'error',
355
+ });
356
+ }
325
357
  });
326
358
  EventsUI.onClick(
327
359
  `.btn-file-delete-${params.data._id}`,
@@ -371,6 +403,80 @@ const FileExplorer = {
371
403
  },
372
404
  { context: 'modal' },
373
405
  );
406
+
407
+ // Toggle public/private status
408
+ EventsUI.onClick(
409
+ `.${toggleId}`,
410
+ async (e) => {
411
+ e.preventDefault();
412
+
413
+ // If document is currently private, show confirmation before making public
414
+ if (!params.data.isPublic) {
415
+ const confirmResult = await Modal.RenderConfirm({
416
+ html: async () => {
417
+ return html`
418
+ <div class="in section-mp" style="text-align: center">
419
+ ${Translate.Render('confirm-make-public')}
420
+ <br />
421
+ "${params.data.title}"
422
+ </div>
423
+ `;
424
+ },
425
+ id: `confirm-toggle-public-${params.data._id}`,
426
+ });
427
+ if (confirmResult.status !== 'confirm') return;
428
+ }
429
+
430
+ try {
431
+ const { data, status } = await DocumentService.patch({
432
+ id: params.data._id,
433
+ action: 'toggle-public',
434
+ });
435
+
436
+ if (status === 'success') {
437
+ // Update local data
438
+ params.data.isPublic = data.isPublic;
439
+
440
+ // Update documentInstance
441
+ const docIndex = documentInstance.findIndex((d) => d._id === params.data._id);
442
+ if (docIndex !== -1) {
443
+ documentInstance[docIndex].isPublic = data.isPublic;
444
+ }
445
+
446
+ // Update button icon
447
+ const btnElement = s(`.${toggleId}`);
448
+ if (btnElement) {
449
+ const iconElement = btnElement.querySelector('i');
450
+ if (iconElement) {
451
+ if (data.isPublic) {
452
+ iconElement.className = 'fas fa-globe';
453
+ iconElement.style.color = '#4caf50';
454
+ } else {
455
+ iconElement.className = 'fas fa-lock';
456
+ iconElement.style.color = '#9e9e9e';
457
+ }
458
+ }
459
+ }
460
+
461
+ NotificationManager.Push({
462
+ html: data.isPublic
463
+ ? Translate.Render('document-now-public')
464
+ : Translate.Render('document-now-private'),
465
+ status: 'success',
466
+ });
467
+ } else {
468
+ throw new Error('Failed to toggle public status');
469
+ }
470
+ } catch (error) {
471
+ logger.error('Toggle public failed:', error);
472
+ NotificationManager.Push({
473
+ html: Translate.Render('error-toggle-public'),
474
+ status: 'error',
475
+ });
476
+ }
477
+ },
478
+ { context: 'modal' },
479
+ );
374
480
  });
375
481
  }
376
482
 
@@ -618,7 +724,7 @@ const FileExplorer = {
618
724
  columnDefs: [
619
725
  { field: 'name', flex: 2, headerName: 'Name', cellRenderer: LoadFileNameRenderer },
620
726
  { field: 'mimetype', flex: 1, headerName: 'Type' },
621
- { headerName: '', width: 80, cellRenderer: LoadFileActionsRenderer },
727
+ { headerName: '', width: 120, cellRenderer: LoadFileActionsRenderer },
622
728
  ],
623
729
  },
624
730
  })}
@@ -680,6 +786,7 @@ const FileExplorer = {
680
786
  fileId: f.fileId._id,
681
787
  _id: f._id,
682
788
  title: f.title,
789
+ isPublic: f.isPublic || false,
683
790
  };
684
791
  });
685
792
  let documentId = document._id;