@underpostnet/underpost 2.97.1 → 2.98.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/cli.md +3 -1
- package/conf.js +2 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +1 -1
- package/scripts/rocky-pwa.sh +200 -0
- package/src/api/core/core.service.js +0 -5
- package/src/api/default/default.service.js +7 -5
- package/src/api/document/document.model.js +1 -1
- package/src/api/document/document.router.js +5 -0
- package/src/api/document/document.service.js +176 -128
- package/src/api/file/file.model.js +112 -4
- package/src/api/file/file.ref.json +42 -0
- package/src/api/file/file.service.js +380 -32
- package/src/api/user/user.model.js +38 -1
- package/src/api/user/user.router.js +96 -63
- package/src/api/user/user.service.js +81 -48
- package/src/cli/db.js +424 -166
- package/src/cli/index.js +8 -0
- package/src/cli/repository.js +1 -1
- package/src/cli/run.js +1 -0
- package/src/cli/ssh.js +10 -10
- package/src/client/components/core/Account.js +327 -36
- package/src/client/components/core/AgGrid.js +3 -0
- package/src/client/components/core/Auth.js +11 -3
- package/src/client/components/core/Chat.js +2 -2
- package/src/client/components/core/Content.js +161 -80
- package/src/client/components/core/Css.js +30 -0
- package/src/client/components/core/CssCore.js +16 -12
- package/src/client/components/core/FileExplorer.js +813 -49
- package/src/client/components/core/Input.js +207 -12
- package/src/client/components/core/LogIn.js +42 -20
- package/src/client/components/core/Modal.js +138 -24
- package/src/client/components/core/Panel.js +71 -32
- package/src/client/components/core/PanelForm.js +262 -77
- package/src/client/components/core/PublicProfile.js +888 -0
- package/src/client/components/core/Responsive.js +15 -7
- package/src/client/components/core/Router.js +117 -15
- package/src/client/components/core/SearchBox.js +322 -116
- package/src/client/components/core/SignUp.js +26 -7
- package/src/client/components/core/SocketIo.js +6 -3
- package/src/client/components/core/Translate.js +148 -0
- package/src/client/components/core/Validator.js +15 -0
- package/src/client/components/core/windowGetDimensions.js +6 -6
- package/src/client/components/default/MenuDefault.js +59 -12
- package/src/client/components/default/RoutesDefault.js +1 -0
- package/src/client/services/core/core.service.js +163 -1
- package/src/client/services/default/default.management.js +454 -76
- package/src/client/services/default/default.service.js +13 -6
- package/src/client/services/file/file.service.js +43 -16
- package/src/client/services/user/user.service.js +13 -9
- package/src/client/sw/default.sw.js +107 -184
- package/src/db/DataBaseProvider.js +1 -1
- package/src/db/mongo/MongooseDB.js +1 -1
- package/src/index.js +1 -1
- package/src/mailer/MailerProvider.js +4 -4
- package/src/runtime/express/Express.js +2 -1
- package/src/runtime/lampp/Lampp.js +2 -2
- package/src/server/auth.js +3 -6
- package/src/server/data-query.js +449 -0
- package/src/server/object-layer.js +0 -3
- package/src/ws/IoInterface.js +2 -2
|
@@ -5,17 +5,17 @@ import { AgGrid } from './AgGrid.js';
|
|
|
5
5
|
import { Auth } from './Auth.js';
|
|
6
6
|
import { BtnIcon } from './BtnIcon.js';
|
|
7
7
|
import { getSubpaths, uniqueArray } from './CommonJs.js';
|
|
8
|
-
import { darkTheme, renderCssAttr } from './Css.js';
|
|
8
|
+
import { Css, darkTheme, dynamicCol, renderCssAttr, Themes } from './Css.js';
|
|
9
9
|
import { EventsUI } from './EventsUI.js';
|
|
10
10
|
import { fileFormDataFactory, Input, InputFile } from './Input.js';
|
|
11
11
|
import { loggerFactory } from './Logger.js';
|
|
12
|
-
import { Modal } from './Modal.js';
|
|
12
|
+
import { Modal, renderViewTitle } from './Modal.js';
|
|
13
13
|
import { NotificationManager } from './NotificationManager.js';
|
|
14
14
|
import { RouterEvents } from './Router.js';
|
|
15
15
|
import { Translate } from './Translate.js';
|
|
16
16
|
import { Validator } from './Validator.js';
|
|
17
17
|
import { copyData, downloadFile, s } from './VanillaJs.js';
|
|
18
|
-
import { getProxyPath, getQueryParams, setPath } from './Router.js';
|
|
18
|
+
import { getProxyPath, getQueryParams, setPath, setQueryParams, listenQueryParamsChange } from './Router.js';
|
|
19
19
|
|
|
20
20
|
const logger = loggerFactory(import.meta);
|
|
21
21
|
|
|
@@ -94,6 +94,22 @@ const FileExplorer = {
|
|
|
94
94
|
const query = getQueryParams();
|
|
95
95
|
let location = query?.location ? this.locationFormat({ f: query }) : '/';
|
|
96
96
|
let files, folders, documentId, documentInstance;
|
|
97
|
+
|
|
98
|
+
// Simple pagination state
|
|
99
|
+
const PAGE_SIZE = 5;
|
|
100
|
+
let currentPage = query?.page ? parseInt(query.page) - 1 : 0;
|
|
101
|
+
if (currentPage < 0) currentPage = 0;
|
|
102
|
+
let displayedFiles = [];
|
|
103
|
+
|
|
104
|
+
// Search filter state - initialize from URL query param
|
|
105
|
+
let searchFilters = {
|
|
106
|
+
title: query?.title || '',
|
|
107
|
+
mdFile: query?.mdFile || '',
|
|
108
|
+
file: query?.file || '',
|
|
109
|
+
};
|
|
110
|
+
let filteredFiles = [];
|
|
111
|
+
let isProcessingQueryChange = false; // Prevent recursion during URL sync
|
|
112
|
+
const queryParamsListenerId = `file-explorer-${idModal}`;
|
|
97
113
|
const cleanData = () => {
|
|
98
114
|
files = [];
|
|
99
115
|
folders = [];
|
|
@@ -101,27 +117,89 @@ const FileExplorer = {
|
|
|
101
117
|
documentInstance = [];
|
|
102
118
|
};
|
|
103
119
|
cleanData();
|
|
120
|
+
const applySearchFilter = () => {
|
|
121
|
+
filteredFiles = files;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const updatePaginationUI = () => {
|
|
125
|
+
const paginationInfo = s(`.file-explorer-pagination-info`);
|
|
126
|
+
const prevBtn = s(`.file-explorer-prev-btn`);
|
|
127
|
+
const nextBtn = s(`.file-explorer-next-btn`);
|
|
128
|
+
if (paginationInfo) {
|
|
129
|
+
const totalPages = Math.ceil(filteredFiles.length / PAGE_SIZE);
|
|
130
|
+
const showing =
|
|
131
|
+
searchFilters.title || searchFilters.mdFile || searchFilters.file
|
|
132
|
+
? `${filteredFiles.length}/${files.length}`
|
|
133
|
+
: `${files.length}`;
|
|
134
|
+
paginationInfo.textContent = `${currentPage + 1} / ${totalPages || 1} (${showing} files)`;
|
|
135
|
+
}
|
|
136
|
+
if (prevBtn) {
|
|
137
|
+
prevBtn.disabled = currentPage === 0;
|
|
138
|
+
prevBtn.style.opacity = currentPage === 0 ? '0.5' : '1';
|
|
139
|
+
prevBtn.style.cursor = currentPage === 0 ? 'not-allowed' : 'pointer';
|
|
140
|
+
}
|
|
141
|
+
if (nextBtn) {
|
|
142
|
+
const isDisabled = (currentPage + 1) * PAGE_SIZE >= filteredFiles.length;
|
|
143
|
+
nextBtn.disabled = isDisabled;
|
|
144
|
+
nextBtn.style.opacity = isDisabled ? '0.5' : '1';
|
|
145
|
+
nextBtn.style.cursor = isDisabled ? 'not-allowed' : 'pointer';
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const getPagedFiles = () => {
|
|
150
|
+
const start = currentPage * PAGE_SIZE;
|
|
151
|
+
const end = start + PAGE_SIZE;
|
|
152
|
+
return filteredFiles.slice(start, end);
|
|
153
|
+
};
|
|
154
|
+
|
|
104
155
|
FileExplorer.Api[idModal].displayList = async () => {
|
|
105
156
|
if (!s(`.${idModal}`)) return;
|
|
106
157
|
const query = getQueryParams();
|
|
107
158
|
location = query?.location ? this.locationFormat({ f: query }) : '/';
|
|
108
159
|
s(`.file-explorer-query-nav`).value = location;
|
|
109
|
-
|
|
160
|
+
|
|
161
|
+
// Sync search filters from URL
|
|
162
|
+
searchFilters = {
|
|
163
|
+
title: query?.title || '',
|
|
164
|
+
mdFile: query?.mdFile || '',
|
|
165
|
+
file: query?.file || '',
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
if (s(`.file-explorer-search-title`)) s(`.file-explorer-search-title`).value = searchFilters.title;
|
|
169
|
+
if (s(`.file-explorer-search-md-file`)) s(`.file-explorer-search-md-file`).value = searchFilters.mdFile;
|
|
170
|
+
if (s(`.file-explorer-search-file`)) s(`.file-explorer-search-file`).value = searchFilters.file;
|
|
171
|
+
|
|
172
|
+
const format = this.documentDataFormat({ document: documentInstance, location, searchFilters });
|
|
110
173
|
files = format.files;
|
|
111
174
|
folders = format.folders;
|
|
112
|
-
|
|
175
|
+
applySearchFilter();
|
|
176
|
+
const queryPage = query?.page ? parseInt(query.page) - 1 : 0;
|
|
177
|
+
currentPage = queryPage >= 0 ? queryPage : 0;
|
|
178
|
+
displayedFiles = getPagedFiles();
|
|
179
|
+
AgGrid.grids[gridFileId].setGridOption('rowData', displayedFiles);
|
|
113
180
|
AgGrid.grids[gridFolderId].setGridOption('rowData', folders);
|
|
181
|
+
updatePaginationUI();
|
|
114
182
|
};
|
|
115
183
|
FileExplorer.Api[idModal].updateData = async (optionsUpdate = { display: false }) => {
|
|
116
184
|
if (!s(`.${idModal}`)) return;
|
|
117
185
|
if (Auth.getToken()) {
|
|
118
186
|
try {
|
|
119
|
-
const { status, data:
|
|
120
|
-
|
|
187
|
+
const { status, data: responseData } = await DocumentService.get({
|
|
188
|
+
params: {
|
|
189
|
+
searchTitle: searchFilters.title,
|
|
190
|
+
searchMdFile: searchFilters.mdFile,
|
|
191
|
+
searchFile: searchFilters.file,
|
|
192
|
+
location,
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
// Handle both old format (array) and new format with pagination
|
|
196
|
+
const document = Array.isArray(responseData) ? responseData : responseData.data || [];
|
|
197
|
+
const format = this.documentDataFormat({ document, location, searchFilters });
|
|
121
198
|
files = format.files;
|
|
122
199
|
documentId = format.documentId;
|
|
123
200
|
folders = format.folders;
|
|
124
201
|
documentInstance = document;
|
|
202
|
+
applySearchFilter();
|
|
125
203
|
} catch (error) {
|
|
126
204
|
logger.error(error);
|
|
127
205
|
NotificationManager.Push({
|
|
@@ -143,6 +221,180 @@ const FileExplorer = {
|
|
|
143
221
|
});
|
|
144
222
|
};
|
|
145
223
|
|
|
224
|
+
// Listen for query param changes (browser back/forward navigation)
|
|
225
|
+
listenQueryParamsChange({
|
|
226
|
+
id: queryParamsListenerId,
|
|
227
|
+
event: async (queryParams) => {
|
|
228
|
+
if (!s(`.${idModal}`)) return;
|
|
229
|
+
if (isProcessingQueryChange) return;
|
|
230
|
+
|
|
231
|
+
const tab = queryParams?.tab || '';
|
|
232
|
+
if (tab === 'upload') {
|
|
233
|
+
s(`.file-explorer-nav`).style.display = 'none';
|
|
234
|
+
s(`.file-explorer-uploader`).style.display = 'block';
|
|
235
|
+
} else {
|
|
236
|
+
s(`.file-explorer-nav`).style.display = 'block';
|
|
237
|
+
s(`.file-explorer-uploader`).style.display = 'none';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const page = queryParams?.page ? parseInt(queryParams.page) - 1 : 0;
|
|
241
|
+
if (page !== currentPage) {
|
|
242
|
+
currentPage = page >= 0 ? page : 0;
|
|
243
|
+
displayedFiles = getPagedFiles();
|
|
244
|
+
if (AgGrid.grids[gridFileId]) {
|
|
245
|
+
AgGrid.grids[gridFileId].setGridOption('rowData', displayedFiles);
|
|
246
|
+
}
|
|
247
|
+
updatePaginationUI();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const newFilters = {
|
|
251
|
+
title: queryParams?.title || '',
|
|
252
|
+
mdFile: queryParams?.mdFile || '',
|
|
253
|
+
file: queryParams?.file || '',
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
if (
|
|
257
|
+
newFilters.title !== searchFilters.title ||
|
|
258
|
+
newFilters.mdFile !== searchFilters.mdFile ||
|
|
259
|
+
newFilters.file !== searchFilters.file
|
|
260
|
+
) {
|
|
261
|
+
isProcessingQueryChange = true;
|
|
262
|
+
searchFilters = newFilters;
|
|
263
|
+
|
|
264
|
+
if (s(`.file-explorer-search-title`)) s(`.file-explorer-search-title`).value = searchFilters.title;
|
|
265
|
+
if (s(`.file-explorer-search-md-file`)) s(`.file-explorer-search-md-file`).value = searchFilters.mdFile;
|
|
266
|
+
if (s(`.file-explorer-search-file`)) s(`.file-explorer-search-file`).value = searchFilters.file;
|
|
267
|
+
|
|
268
|
+
await FileExplorer.Api[idModal].updateData({ display: true });
|
|
269
|
+
|
|
270
|
+
setTimeout(() => {
|
|
271
|
+
isProcessingQueryChange = false;
|
|
272
|
+
}, 100);
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Pagination button event handlers
|
|
278
|
+
setTimeout(() => {
|
|
279
|
+
EventsUI.onClick(`.file-explorer-prev-btn`, (e) => {
|
|
280
|
+
e.preventDefault();
|
|
281
|
+
if (currentPage > 0) {
|
|
282
|
+
setQueryParams({ page: currentPage }, { replace: false });
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
EventsUI.onClick(`.file-explorer-next-btn`, (e) => {
|
|
287
|
+
e.preventDefault();
|
|
288
|
+
if ((currentPage + 1) * PAGE_SIZE < filteredFiles.length) {
|
|
289
|
+
setQueryParams({ page: currentPage + 2 }, { replace: false });
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// Search input handlers
|
|
294
|
+
let searchTimeout;
|
|
295
|
+
|
|
296
|
+
const setupSearchInput = (selector, key) => {
|
|
297
|
+
const el = s(selector);
|
|
298
|
+
if (el) {
|
|
299
|
+
if (searchFilters[key]) el.value = searchFilters[key];
|
|
300
|
+
el.addEventListener('input', (e) => {
|
|
301
|
+
clearTimeout(searchTimeout);
|
|
302
|
+
searchTimeout = setTimeout(async () => {
|
|
303
|
+
const val = e.target.value.trim();
|
|
304
|
+
if (val === searchFilters[key]) return;
|
|
305
|
+
|
|
306
|
+
isProcessingQueryChange = true;
|
|
307
|
+
searchFilters[key] = val;
|
|
308
|
+
await FileExplorer.Api[idModal].updateData({ display: true });
|
|
309
|
+
|
|
310
|
+
const queryParams = {};
|
|
311
|
+
if (searchFilters.title) queryParams.title = searchFilters.title;
|
|
312
|
+
if (searchFilters.mdFile) queryParams.mdFile = searchFilters.mdFile;
|
|
313
|
+
if (searchFilters.file) queryParams.file = searchFilters.file;
|
|
314
|
+
|
|
315
|
+
if (!searchFilters.title) queryParams.title = null;
|
|
316
|
+
if (!searchFilters.mdFile) queryParams.mdFile = null;
|
|
317
|
+
if (!searchFilters.file) queryParams.file = null;
|
|
318
|
+
|
|
319
|
+
queryParams.page = 1;
|
|
320
|
+
|
|
321
|
+
setQueryParams(queryParams, { replace: false });
|
|
322
|
+
|
|
323
|
+
setTimeout(() => {
|
|
324
|
+
isProcessingQueryChange = false;
|
|
325
|
+
}, 100);
|
|
326
|
+
}, 300);
|
|
327
|
+
});
|
|
328
|
+
el.addEventListener('keydown', (e) => {
|
|
329
|
+
if (e.key === 'Enter') e.preventDefault();
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
setupSearchInput(`.file-explorer-search-title`, 'title');
|
|
335
|
+
setupSearchInput(`.file-explorer-search-md-file`, 'mdFile');
|
|
336
|
+
setupSearchInput(`.file-explorer-search-file`, 'file');
|
|
337
|
+
|
|
338
|
+
// Submit search button
|
|
339
|
+
EventsUI.onClick(`.file-explorer-search-submit`, async (e) => {
|
|
340
|
+
e.preventDefault();
|
|
341
|
+
clearTimeout(searchTimeout);
|
|
342
|
+
|
|
343
|
+
const titleVal = s(`.file-explorer-search-title`)?.value.trim() || '';
|
|
344
|
+
const mdFileVal = s(`.file-explorer-search-md-file`)?.value.trim() || '';
|
|
345
|
+
const fileVal = s(`.file-explorer-search-file`)?.value.trim() || '';
|
|
346
|
+
|
|
347
|
+
searchFilters = { title: titleVal, mdFile: mdFileVal, file: fileVal };
|
|
348
|
+
|
|
349
|
+
isProcessingQueryChange = true;
|
|
350
|
+
|
|
351
|
+
const queryParams = {};
|
|
352
|
+
if (searchFilters.title) queryParams.title = searchFilters.title;
|
|
353
|
+
if (searchFilters.mdFile) queryParams.mdFile = searchFilters.mdFile;
|
|
354
|
+
if (searchFilters.file) queryParams.file = searchFilters.file;
|
|
355
|
+
|
|
356
|
+
if (!searchFilters.title) queryParams.title = null;
|
|
357
|
+
if (!searchFilters.mdFile) queryParams.mdFile = null;
|
|
358
|
+
if (!searchFilters.file) queryParams.file = null;
|
|
359
|
+
|
|
360
|
+
queryParams.page = 1;
|
|
361
|
+
|
|
362
|
+
setQueryParams(queryParams, { replace: false });
|
|
363
|
+
|
|
364
|
+
await FileExplorer.Api[idModal].updateData({ display: true });
|
|
365
|
+
|
|
366
|
+
setTimeout(() => {
|
|
367
|
+
isProcessingQueryChange = false;
|
|
368
|
+
}, 100);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// Clear search button
|
|
372
|
+
EventsUI.onClick(`.file-explorer-search-clear`, (e) => {
|
|
373
|
+
e.preventDefault();
|
|
374
|
+
|
|
375
|
+
if (!searchFilters.title && !searchFilters.mdFile && !searchFilters.file) return;
|
|
376
|
+
|
|
377
|
+
isProcessingQueryChange = true;
|
|
378
|
+
if (s(`.file-explorer-search-title`)) s(`.file-explorer-search-title`).value = '';
|
|
379
|
+
if (s(`.file-explorer-search-md-file`)) s(`.file-explorer-search-md-file`).value = '';
|
|
380
|
+
if (s(`.file-explorer-search-file`)) s(`.file-explorer-search-file`).value = '';
|
|
381
|
+
|
|
382
|
+
searchFilters = { title: '', mdFile: '', file: '' };
|
|
383
|
+
|
|
384
|
+
applySearchFilter();
|
|
385
|
+
currentPage = 0;
|
|
386
|
+
displayedFiles = getPagedFiles();
|
|
387
|
+
AgGrid.grids[gridFileId].setGridOption('rowData', displayedFiles);
|
|
388
|
+
updatePaginationUI();
|
|
389
|
+
|
|
390
|
+
setQueryParams({ title: null, mdFile: null, file: null, page: 1 }, { replace: false });
|
|
391
|
+
|
|
392
|
+
setTimeout(() => {
|
|
393
|
+
isProcessingQueryChange = false;
|
|
394
|
+
}, 100);
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
146
398
|
setTimeout(async () => {
|
|
147
399
|
FileExplorer.Api[idModal].updateData({ display: true });
|
|
148
400
|
const formData = [
|
|
@@ -156,6 +408,15 @@ const FileExplorer = {
|
|
|
156
408
|
|
|
157
409
|
EventsUI.onClick(`.btn-input-file-explorer`, async (e) => {
|
|
158
410
|
e.preventDefault();
|
|
411
|
+
|
|
412
|
+
// Check authentication before upload
|
|
413
|
+
if (!Auth.getToken()) {
|
|
414
|
+
return NotificationManager.Push({
|
|
415
|
+
html: Translate.Render(`error-user-not-authenticated`),
|
|
416
|
+
status: 'error',
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
159
420
|
const { errorMessage } = await validators();
|
|
160
421
|
if (!formBodyFiles)
|
|
161
422
|
return NotificationManager.Push({
|
|
@@ -166,6 +427,12 @@ const FileExplorer = {
|
|
|
166
427
|
let fileData;
|
|
167
428
|
{
|
|
168
429
|
const { status, data } = await FileService.post({ body: formBodyFiles });
|
|
430
|
+
if (status === 'error' || !data) {
|
|
431
|
+
return NotificationManager.Push({
|
|
432
|
+
html: Translate.Render(`error-upload-file`),
|
|
433
|
+
status: 'error',
|
|
434
|
+
});
|
|
435
|
+
}
|
|
169
436
|
fileData = data;
|
|
170
437
|
}
|
|
171
438
|
{
|
|
@@ -189,15 +456,25 @@ const FileExplorer = {
|
|
|
189
456
|
const format = this.documentDataFormat({ document: documentInstance, location });
|
|
190
457
|
files = format.files;
|
|
191
458
|
folders = format.folders;
|
|
192
|
-
|
|
459
|
+
applySearchFilter();
|
|
460
|
+
currentPage = 0;
|
|
461
|
+
displayedFiles = getPagedFiles();
|
|
462
|
+
AgGrid.grids[gridFileId].setGridOption('rowData', displayedFiles);
|
|
193
463
|
AgGrid.grids[gridFolderId].setGridOption('rowData', folders);
|
|
464
|
+
updatePaginationUI();
|
|
194
465
|
NotificationManager.Push({
|
|
195
466
|
html: Translate.Render(`${status}-upload-file`),
|
|
196
467
|
status,
|
|
197
468
|
});
|
|
198
469
|
if (status === 'success') {
|
|
199
|
-
|
|
470
|
+
// Clear the file input
|
|
200
471
|
s(`.btn-clear-input-file-${idDropFileInput}`).click();
|
|
472
|
+
// Switch to explorer view with the uploaded location
|
|
473
|
+
setQueryParams({ tab: null, location: location }, { replace: false });
|
|
474
|
+
// Show explorer view, hide upload view
|
|
475
|
+
s(`.file-explorer-nav`).style.display = 'block';
|
|
476
|
+
s(`.file-explorer-uploader`).style.display = 'none';
|
|
477
|
+
s(`.file-explorer-query-nav`).value = location;
|
|
201
478
|
}
|
|
202
479
|
}
|
|
203
480
|
});
|
|
@@ -214,19 +491,26 @@ const FileExplorer = {
|
|
|
214
491
|
});
|
|
215
492
|
EventsUI.onClick(`.btn-input-home-directory`, async (e) => {
|
|
216
493
|
e.preventDefault();
|
|
494
|
+
|
|
495
|
+
if (getQueryParams()?.tab === 'upload') {
|
|
496
|
+
setQueryParams({ tab: null }, { replace: false });
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
|
|
217
500
|
let newLocation = '/';
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
s(`.file-explorer-nav`).style.display = 'block';
|
|
221
|
-
} else if (newLocation === location) return;
|
|
222
|
-
else location = newLocation;
|
|
501
|
+
if (newLocation === location) return;
|
|
502
|
+
location = newLocation;
|
|
223
503
|
setPath(`${window.location.pathname}?location=${location}`);
|
|
224
504
|
s(`.file-explorer-query-nav`).value = location;
|
|
225
505
|
const format = this.documentDataFormat({ document: documentInstance, location });
|
|
226
506
|
files = format.files;
|
|
227
507
|
folders = format.folders;
|
|
228
|
-
|
|
508
|
+
applySearchFilter();
|
|
509
|
+
currentPage = 0;
|
|
510
|
+
displayedFiles = getPagedFiles();
|
|
511
|
+
AgGrid.grids[gridFileId].setGridOption('rowData', displayedFiles);
|
|
229
512
|
AgGrid.grids[gridFolderId].setGridOption('rowData', folders);
|
|
513
|
+
updatePaginationUI();
|
|
230
514
|
});
|
|
231
515
|
EventsUI.onClick(`.btn-input-copy-directory`, async (e) => {
|
|
232
516
|
e.preventDefault();
|
|
@@ -246,8 +530,7 @@ const FileExplorer = {
|
|
|
246
530
|
});
|
|
247
531
|
EventsUI.onClick(`.btn-input-upload-file`, async (e) => {
|
|
248
532
|
e.preventDefault();
|
|
249
|
-
|
|
250
|
-
s(`.file-explorer-uploader`).style.display = 'block';
|
|
533
|
+
setQueryParams({ tab: 'upload' }, { replace: false });
|
|
251
534
|
});
|
|
252
535
|
});
|
|
253
536
|
|
|
@@ -259,6 +542,8 @@ const FileExplorer = {
|
|
|
259
542
|
// params.data._id
|
|
260
543
|
|
|
261
544
|
this.eGui = document.createElement('div');
|
|
545
|
+
const isPublic = params.data.isPublic;
|
|
546
|
+
const toggleId = `toggle-public-${params.data._id}`;
|
|
262
547
|
this.eGui.innerHTML = html`
|
|
263
548
|
<div class="fl">
|
|
264
549
|
${await BtnIcon.Render({
|
|
@@ -281,6 +566,18 @@ const FileExplorer = {
|
|
|
281
566
|
label: html`<i class="fas fa-copy"></i>`,
|
|
282
567
|
type: 'button',
|
|
283
568
|
})}
|
|
569
|
+
${await BtnIcon.Render({
|
|
570
|
+
class: `in fll management-table-btn-mini btn-file-edit-${params.data._id}`,
|
|
571
|
+
label: html`<i class="fas fa-edit"></i>`,
|
|
572
|
+
type: 'button',
|
|
573
|
+
})}
|
|
574
|
+
${await BtnIcon.Render({
|
|
575
|
+
class: `in fll management-table-btn-mini ${toggleId}`,
|
|
576
|
+
label: isPublic
|
|
577
|
+
? html`<i class="fas fa-globe" style="color: #4caf50;"></i>`
|
|
578
|
+
: html`<i class="fas fa-lock" style="color: #9e9e9e;"></i>`,
|
|
579
|
+
type: 'button',
|
|
580
|
+
})}
|
|
284
581
|
</div>
|
|
285
582
|
`;
|
|
286
583
|
|
|
@@ -289,7 +586,10 @@ const FileExplorer = {
|
|
|
289
586
|
const url = `${window.location.origin}${uri}`;
|
|
290
587
|
|
|
291
588
|
const originObj = documentInstance.find((d) => d._id === params.data._id);
|
|
292
|
-
const blobUri =
|
|
589
|
+
const blobUri =
|
|
590
|
+
originObj && originObj.fileId
|
|
591
|
+
? getApiBaseUrl({ id: originObj.fileId._id, endpoint: 'file/blob' })
|
|
592
|
+
: undefined;
|
|
293
593
|
|
|
294
594
|
if (!originObj) {
|
|
295
595
|
s(`.btn-file-view-${params.data._id}`).classList.add('hide');
|
|
@@ -315,13 +615,21 @@ const FileExplorer = {
|
|
|
315
615
|
|
|
316
616
|
EventsUI.onClick(`.btn-file-download-${params.data._id}`, async (e) => {
|
|
317
617
|
e.preventDefault();
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
data:
|
|
321
|
-
status
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
618
|
+
try {
|
|
619
|
+
// Use FileService with blob/ prefix for centralized blob fetching
|
|
620
|
+
const { data: blobArray, status } = await FileService.get({ id: `blob/${params.data.fileId}` });
|
|
621
|
+
if (status === 'success' && blobArray && blobArray[0]) {
|
|
622
|
+
downloadFile(blobArray[0], params.data.name);
|
|
623
|
+
} else {
|
|
624
|
+
throw new Error('Failed to fetch file blob');
|
|
625
|
+
}
|
|
626
|
+
} catch (error) {
|
|
627
|
+
logger.error('Download failed:', error);
|
|
628
|
+
NotificationManager.Push({
|
|
629
|
+
html: 'Download failed',
|
|
630
|
+
status: 'error',
|
|
631
|
+
});
|
|
632
|
+
}
|
|
325
633
|
});
|
|
326
634
|
EventsUI.onClick(
|
|
327
635
|
`.btn-file-delete-${params.data._id}`,
|
|
@@ -364,10 +672,367 @@ const FileExplorer = {
|
|
|
364
672
|
const format = FileExplorer.documentDataFormat({ document: documentInstance, location });
|
|
365
673
|
files = format.files;
|
|
366
674
|
folders = format.folders;
|
|
675
|
+
applySearchFilter();
|
|
367
676
|
// AgGrid.grids[gridFileId].setGridOption('rowData', files);
|
|
368
677
|
// const selectedData = gridApi.getSelectedRows();
|
|
369
678
|
AgGrid.grids[gridFileId].applyTransaction({ remove: [params.data] });
|
|
370
679
|
AgGrid.grids[gridFolderId].setGridOption('rowData', folders);
|
|
680
|
+
updatePaginationUI();
|
|
681
|
+
},
|
|
682
|
+
{ context: 'modal' },
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
// Toggle public/private status
|
|
686
|
+
EventsUI.onClick(
|
|
687
|
+
`.${toggleId}`,
|
|
688
|
+
async (e) => {
|
|
689
|
+
e.preventDefault();
|
|
690
|
+
|
|
691
|
+
// If document is currently private, show confirmation before making public
|
|
692
|
+
if (!params.data.isPublic) {
|
|
693
|
+
const confirmResult = await Modal.RenderConfirm({
|
|
694
|
+
html: async () => {
|
|
695
|
+
return html`
|
|
696
|
+
<div class="in section-mp" style="text-align: center">
|
|
697
|
+
${Translate.Render('confirm-make-public')}
|
|
698
|
+
<br />
|
|
699
|
+
"${params.data.title}"
|
|
700
|
+
</div>
|
|
701
|
+
`;
|
|
702
|
+
},
|
|
703
|
+
id: `confirm-toggle-public-${params.data._id}`,
|
|
704
|
+
});
|
|
705
|
+
if (confirmResult.status !== 'confirm') return;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
try {
|
|
709
|
+
const { data, status } = await DocumentService.patch({
|
|
710
|
+
id: params.data._id,
|
|
711
|
+
action: 'toggle-public',
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
if (status === 'success') {
|
|
715
|
+
// Update local data
|
|
716
|
+
params.data.isPublic = data.isPublic;
|
|
717
|
+
|
|
718
|
+
// Update documentInstance
|
|
719
|
+
const docIndex = documentInstance.findIndex((d) => d._id === params.data._id);
|
|
720
|
+
if (docIndex !== -1) {
|
|
721
|
+
documentInstance[docIndex].isPublic = data.isPublic;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Update button icon
|
|
725
|
+
const btnElement = s(`.${toggleId}`);
|
|
726
|
+
if (btnElement) {
|
|
727
|
+
const iconElement = btnElement.querySelector('i');
|
|
728
|
+
if (iconElement) {
|
|
729
|
+
if (data.isPublic) {
|
|
730
|
+
iconElement.className = 'fas fa-globe';
|
|
731
|
+
iconElement.style.color = '#4caf50';
|
|
732
|
+
} else {
|
|
733
|
+
iconElement.className = 'fas fa-lock';
|
|
734
|
+
iconElement.style.color = '#9e9e9e';
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
NotificationManager.Push({
|
|
740
|
+
html: data.isPublic
|
|
741
|
+
? Translate.Render('document-now-public')
|
|
742
|
+
: Translate.Render('document-now-private'),
|
|
743
|
+
status: 'success',
|
|
744
|
+
});
|
|
745
|
+
} else {
|
|
746
|
+
throw new Error('Failed to toggle public status');
|
|
747
|
+
}
|
|
748
|
+
} catch (error) {
|
|
749
|
+
logger.error('Toggle public failed:', error);
|
|
750
|
+
NotificationManager.Push({
|
|
751
|
+
html: Translate.Render('error-toggle-public'),
|
|
752
|
+
status: 'error',
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
},
|
|
756
|
+
{ context: 'modal' },
|
|
757
|
+
);
|
|
758
|
+
|
|
759
|
+
// Edit document button
|
|
760
|
+
EventsUI.onClick(
|
|
761
|
+
`.btn-file-edit-${params.data._id}`,
|
|
762
|
+
async (e) => {
|
|
763
|
+
e.preventDefault();
|
|
764
|
+
|
|
765
|
+
// Get the original document data from documentInstance
|
|
766
|
+
const originDoc = documentInstance.find((d) => d._id === params.data._id);
|
|
767
|
+
const editModalId = `edit-doc-${params.data._id}`;
|
|
768
|
+
|
|
769
|
+
// Check file existence for proper UX
|
|
770
|
+
const hasMdFile = !!(originDoc && originDoc.mdFileId);
|
|
771
|
+
const hasGenericFile = !!(originDoc && originDoc.fileId);
|
|
772
|
+
|
|
773
|
+
const mdFileMimetype = hasMdFile ? originDoc.mdFileId.mimetype : '';
|
|
774
|
+
const genericFileMimetype = hasGenericFile ? originDoc.fileId.mimetype : '';
|
|
775
|
+
|
|
776
|
+
const editFormHtml = async () => {
|
|
777
|
+
return html`
|
|
778
|
+
<div class="in edit-document-form" style="max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
779
|
+
<!-- Header -->
|
|
780
|
+
<div
|
|
781
|
+
class="in"
|
|
782
|
+
style="text-align: center; margin-bottom: 25px; padding-bottom: 15px; border-bottom: 1px solid rgba(128,128,128,0.3);"
|
|
783
|
+
>
|
|
784
|
+
<p style="color: #888; font-size: 14px; margin: 0;">
|
|
785
|
+
${Translate.Render('editing')}: <strong style="color: inherit;">${params.data.title}</strong>
|
|
786
|
+
</p>
|
|
787
|
+
</div>
|
|
788
|
+
|
|
789
|
+
<!-- Document Title -->
|
|
790
|
+
<div class="in section-mp" style="margin-bottom: 20px;">
|
|
791
|
+
${await Input.Render({
|
|
792
|
+
id: `edit-doc-title-${params.data._id}`,
|
|
793
|
+
type: 'text',
|
|
794
|
+
label: html`<i class="fas fa-heading"></i> ${Translate.Render('doc-title')}`,
|
|
795
|
+
containerClass: 'in section-mp input-container-width',
|
|
796
|
+
placeholder: true,
|
|
797
|
+
value: params.data.title || '',
|
|
798
|
+
})}
|
|
799
|
+
</div>
|
|
800
|
+
|
|
801
|
+
<!-- MD File Name -->
|
|
802
|
+
<div class="in section-mp" style="margin-bottom: 20px;">
|
|
803
|
+
${hasMdFile
|
|
804
|
+
? await Input.Render({
|
|
805
|
+
id: `edit-doc-md-file-${params.data._id}`,
|
|
806
|
+
type: 'text',
|
|
807
|
+
label: html`<i class="fas fa-file-code"></i> ${Translate.Render('md-file-name')}
|
|
808
|
+
<span style="font-size: 11px; color: #888; margin-left: 8px;">(${mdFileMimetype})</span>`,
|
|
809
|
+
containerClass: 'in section-mp input-container-width',
|
|
810
|
+
placeholder: true,
|
|
811
|
+
value: params.data.mdFileName || '',
|
|
812
|
+
})
|
|
813
|
+
: html`
|
|
814
|
+
<div class="in section-mp input-container-width" style="opacity: 0.6;">
|
|
815
|
+
<label style="display: block; margin-bottom: 5px;">
|
|
816
|
+
<i class="fas fa-file-code"></i> ${Translate.Render('md-file-name')}
|
|
817
|
+
</label>
|
|
818
|
+
<div
|
|
819
|
+
style="padding: 10px 12px; border: 1px dashed rgba(128,128,128,0.5); border-radius: 4px; color: #888; font-style: italic;"
|
|
820
|
+
>
|
|
821
|
+
<i class="fas fa-info-circle"></i> ${Translate.Render('no-md-file-attached')}
|
|
822
|
+
</div>
|
|
823
|
+
</div>
|
|
824
|
+
`}
|
|
825
|
+
</div>
|
|
826
|
+
|
|
827
|
+
<!-- Generic File Name -->
|
|
828
|
+
<div class="in section-mp" style="margin-bottom: 20px;">
|
|
829
|
+
${hasGenericFile
|
|
830
|
+
? await Input.Render({
|
|
831
|
+
id: `edit-doc-file-${params.data._id}`,
|
|
832
|
+
type: 'text',
|
|
833
|
+
label: html`<i class="fas fa-file"></i> ${Translate.Render('generic-file-name')}
|
|
834
|
+
<span style="font-size: 11px; color: #888; margin-left: 8px;"
|
|
835
|
+
>(${genericFileMimetype})</span
|
|
836
|
+
>`,
|
|
837
|
+
containerClass: 'in section-mp input-container-width',
|
|
838
|
+
placeholder: true,
|
|
839
|
+
value: params.data.fileName || '',
|
|
840
|
+
})
|
|
841
|
+
: html`
|
|
842
|
+
<div class="in section-mp input-container-width" style="opacity: 0.6;">
|
|
843
|
+
<label style="display: block; margin-bottom: 5px;">
|
|
844
|
+
<i class="fas fa-file"></i> ${Translate.Render('generic-file-name')}
|
|
845
|
+
</label>
|
|
846
|
+
<div
|
|
847
|
+
style="padding: 10px 12px; border: 1px dashed rgba(128,128,128,0.5); border-radius: 4px; color: #888; font-style: italic;"
|
|
848
|
+
>
|
|
849
|
+
<i class="fas fa-info-circle"></i> ${Translate.Render('no-generic-file-attached')}
|
|
850
|
+
</div>
|
|
851
|
+
</div>
|
|
852
|
+
`}
|
|
853
|
+
</div>
|
|
854
|
+
|
|
855
|
+
<!-- Location -->
|
|
856
|
+
<div class="in section-mp" style="margin-bottom: 25px;">
|
|
857
|
+
${await Input.Render({
|
|
858
|
+
id: `edit-doc-location-${params.data._id}`,
|
|
859
|
+
type: 'text',
|
|
860
|
+
label: html`<i class="fas fa-folder"></i> ${Translate.Render('location')}`,
|
|
861
|
+
containerClass: 'in section-mp input-container-width',
|
|
862
|
+
placeholder: true,
|
|
863
|
+
value: params.data.location || '/',
|
|
864
|
+
})}
|
|
865
|
+
</div>
|
|
866
|
+
|
|
867
|
+
<!-- Buttons -->
|
|
868
|
+
<div
|
|
869
|
+
class="fl"
|
|
870
|
+
style="margin-top: 30px; border-top: 1px solid rgba(128,128,128,0.3); padding-top: 20px;"
|
|
871
|
+
>
|
|
872
|
+
<div class="in fll" style="width: 50%; padding: 5px;">
|
|
873
|
+
${await BtnIcon.Render({
|
|
874
|
+
class: `in wfa btn-edit-doc-cancel-${params.data._id}`,
|
|
875
|
+
label: html`<i class="fas fa-times"></i> ${Translate.Render('cancel')}`,
|
|
876
|
+
type: 'button',
|
|
877
|
+
})}
|
|
878
|
+
</div>
|
|
879
|
+
<div class="in fll" style="width: 50%; padding: 5px;">
|
|
880
|
+
${await BtnIcon.Render({
|
|
881
|
+
class: `in wfa btn-edit-doc-submit-${params.data._id}`,
|
|
882
|
+
label: html`<i class="fas fa-save"></i> ${Translate.Render('save')}`,
|
|
883
|
+
type: 'button',
|
|
884
|
+
})}
|
|
885
|
+
</div>
|
|
886
|
+
</div>
|
|
887
|
+
</div>
|
|
888
|
+
`;
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
const { barConfig } = await Themes[Css.currentTheme]();
|
|
892
|
+
|
|
893
|
+
await Modal.Render({
|
|
894
|
+
id: editModalId,
|
|
895
|
+
barConfig,
|
|
896
|
+
title: renderViewTitle({
|
|
897
|
+
icon: html`<i class="fas fa-edit"></i>`,
|
|
898
|
+
text: Translate.Render('edit-document'),
|
|
899
|
+
}),
|
|
900
|
+
html: editFormHtml,
|
|
901
|
+
handleType: 'bar',
|
|
902
|
+
maximize: true,
|
|
903
|
+
mode: 'view',
|
|
904
|
+
slideMenu: 'modal-menu',
|
|
905
|
+
RouterInstance: Modal.Data[options.idModal].options.RouterInstance,
|
|
906
|
+
barMode: Modal.Data[options.idModal].options.barMode,
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
// Handle submit button
|
|
910
|
+
setTimeout(() => {
|
|
911
|
+
EventsUI.onClick(
|
|
912
|
+
`.btn-edit-doc-submit-${params.data._id}`,
|
|
913
|
+
async (ev) => {
|
|
914
|
+
ev.preventDefault();
|
|
915
|
+
|
|
916
|
+
const newTitle = s(`.edit-doc-title-${params.data._id}`).value.trim();
|
|
917
|
+
const newLocation = s(`.edit-doc-location-${params.data._id}`).value.trim();
|
|
918
|
+
|
|
919
|
+
// Get file names only if files exist
|
|
920
|
+
const newMdFileName = hasMdFile ? s(`.edit-doc-md-file-${params.data._id}`)?.value.trim() : null;
|
|
921
|
+
const newFileName = hasGenericFile ? s(`.edit-doc-file-${params.data._id}`)?.value.trim() : null;
|
|
922
|
+
|
|
923
|
+
if (!newTitle) {
|
|
924
|
+
NotificationManager.Push({
|
|
925
|
+
html: Translate.Render('error-title-required'),
|
|
926
|
+
status: 'error',
|
|
927
|
+
});
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
const formattedLocation = FileExplorer.locationFormat({ f: { location: newLocation || '/' } });
|
|
932
|
+
|
|
933
|
+
try {
|
|
934
|
+
const updateBody = {
|
|
935
|
+
title: newTitle,
|
|
936
|
+
location: formattedLocation,
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
// Preserve existing fields from the original document
|
|
940
|
+
if (originDoc) {
|
|
941
|
+
if (originDoc.fileId) updateBody.fileId = originDoc.fileId._id || originDoc.fileId;
|
|
942
|
+
if (originDoc.mdFileId) updateBody.mdFileId = originDoc.mdFileId._id || originDoc.mdFileId;
|
|
943
|
+
if (originDoc.tags) updateBody.tags = originDoc.tags;
|
|
944
|
+
if (typeof originDoc.isPublic !== 'undefined') updateBody.isPublic = originDoc.isPublic;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// Include file name updates if files exist and names changed
|
|
948
|
+
if (hasMdFile && newMdFileName && newMdFileName !== params.data.mdFileName) {
|
|
949
|
+
updateBody.mdFileName = newMdFileName;
|
|
950
|
+
}
|
|
951
|
+
if (hasGenericFile && newFileName && newFileName !== params.data.fileName) {
|
|
952
|
+
updateBody.fileName = newFileName;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
const { data, status } = await DocumentService.put({
|
|
956
|
+
id: params.data._id,
|
|
957
|
+
body: updateBody,
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
if (status === 'success') {
|
|
961
|
+
// Check if location changed
|
|
962
|
+
const locationChanged = formattedLocation !== params.data.location;
|
|
963
|
+
|
|
964
|
+
// Update local data
|
|
965
|
+
params.data.title = newTitle;
|
|
966
|
+
params.data.name = newTitle;
|
|
967
|
+
params.data.location = formattedLocation;
|
|
968
|
+
if (hasMdFile && newMdFileName) params.data.mdFileName = newMdFileName;
|
|
969
|
+
if (hasGenericFile && newFileName) params.data.fileName = newFileName;
|
|
970
|
+
|
|
971
|
+
// Update documentInstance
|
|
972
|
+
const docIndex = documentInstance.findIndex((d) => d._id === params.data._id);
|
|
973
|
+
if (docIndex !== -1) {
|
|
974
|
+
documentInstance[docIndex].title = newTitle;
|
|
975
|
+
documentInstance[docIndex].location = formattedLocation;
|
|
976
|
+
// Update file names in the referenced file objects
|
|
977
|
+
if (hasMdFile && newMdFileName && documentInstance[docIndex].mdFileId) {
|
|
978
|
+
documentInstance[docIndex].mdFileId.name = newMdFileName;
|
|
979
|
+
}
|
|
980
|
+
if (hasGenericFile && newFileName && documentInstance[docIndex].fileId) {
|
|
981
|
+
documentInstance[docIndex].fileId.name = newFileName;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// Refresh the grid with new location
|
|
986
|
+
const format = FileExplorer.documentDataFormat({ document: documentInstance, location });
|
|
987
|
+
files = format.files;
|
|
988
|
+
folders = format.folders;
|
|
989
|
+
applySearchFilter();
|
|
990
|
+
currentPage = 0;
|
|
991
|
+
displayedFiles = getPagedFiles();
|
|
992
|
+
AgGrid.grids[gridFileId].setGridOption('rowData', displayedFiles);
|
|
993
|
+
AgGrid.grids[gridFolderId].setGridOption('rowData', folders);
|
|
994
|
+
updatePaginationUI();
|
|
995
|
+
|
|
996
|
+
NotificationManager.Push({
|
|
997
|
+
html: Translate.Render('success-update-document'),
|
|
998
|
+
status: 'success',
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
// If location changed, navigate to the new location
|
|
1002
|
+
if (!s(`.file-explorer-query-nav`)) s(`.main-btn-cloud`).click();
|
|
1003
|
+
|
|
1004
|
+
if (locationChanged)
|
|
1005
|
+
setTimeout(() => {
|
|
1006
|
+
location = formattedLocation;
|
|
1007
|
+
setPath(`${window.location.pathname}?location=${location}`);
|
|
1008
|
+
s(`.file-explorer-query-nav`).value = location;
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
Modal.removeModal(editModalId);
|
|
1012
|
+
} else {
|
|
1013
|
+
throw new Error('Failed to update document');
|
|
1014
|
+
}
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
logger.error('Update document failed:', error);
|
|
1017
|
+
NotificationManager.Push({
|
|
1018
|
+
html: Translate.Render('error-update-document'),
|
|
1019
|
+
status: 'error',
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
},
|
|
1023
|
+
{ context: 'modal' },
|
|
1024
|
+
);
|
|
1025
|
+
|
|
1026
|
+
// Handle cancel button
|
|
1027
|
+
EventsUI.onClick(
|
|
1028
|
+
`.btn-edit-doc-cancel-${params.data._id}`,
|
|
1029
|
+
async (ev) => {
|
|
1030
|
+
ev.preventDefault();
|
|
1031
|
+
Modal.removeModal(editModalId);
|
|
1032
|
+
},
|
|
1033
|
+
{ context: 'modal' },
|
|
1034
|
+
);
|
|
1035
|
+
});
|
|
371
1036
|
},
|
|
372
1037
|
{ context: 'modal' },
|
|
373
1038
|
);
|
|
@@ -446,8 +1111,12 @@ const FileExplorer = {
|
|
|
446
1111
|
const format = FileExplorer.documentDataFormat({ document: documentInstance, location });
|
|
447
1112
|
files = format.files;
|
|
448
1113
|
folders = format.folders;
|
|
449
|
-
|
|
1114
|
+
applySearchFilter();
|
|
1115
|
+
currentPage = 0;
|
|
1116
|
+
displayedFiles = getPagedFiles();
|
|
1117
|
+
AgGrid.grids[gridFileId].setGridOption('rowData', displayedFiles);
|
|
450
1118
|
AgGrid.grids[gridFolderId].setGridOption('rowData', folders);
|
|
1119
|
+
updatePaginationUI();
|
|
451
1120
|
},
|
|
452
1121
|
{ context: 'modal' },
|
|
453
1122
|
);
|
|
@@ -514,6 +1183,57 @@ const FileExplorer = {
|
|
|
514
1183
|
value: location,
|
|
515
1184
|
})}
|
|
516
1185
|
</div>
|
|
1186
|
+
${dynamicCol({
|
|
1187
|
+
containerSelector: 'file-explorer-search-container',
|
|
1188
|
+
id: 'file-explorer-search',
|
|
1189
|
+
type: 'search-inputs',
|
|
1190
|
+
})}
|
|
1191
|
+
<div class="fl file-explorer-search-container">
|
|
1192
|
+
<div class="in fll file-explorer-search-col-a">
|
|
1193
|
+
${await Input.Render({
|
|
1194
|
+
id: `file-explorer-search-title`,
|
|
1195
|
+
type: 'text',
|
|
1196
|
+
label: html`<i class="fas fa-search"></i> ${Translate.Render('doc-title')}`,
|
|
1197
|
+
containerClass: 'in section-mp input-container-width',
|
|
1198
|
+
placeholder: true,
|
|
1199
|
+
})}
|
|
1200
|
+
</div>
|
|
1201
|
+
<div class="in fll file-explorer-search-col-b">
|
|
1202
|
+
${await Input.Render({
|
|
1203
|
+
id: `file-explorer-search-md-file`,
|
|
1204
|
+
type: 'text',
|
|
1205
|
+
label: html`<i class="fas fa-file-code"></i> ${Translate.Render('md-file-name')}`,
|
|
1206
|
+
containerClass: 'in section-mp input-container-width',
|
|
1207
|
+
placeholder: true,
|
|
1208
|
+
})}
|
|
1209
|
+
</div>
|
|
1210
|
+
<div class="in fll file-explorer-search-col-c">
|
|
1211
|
+
${await Input.Render({
|
|
1212
|
+
id: `file-explorer-search-file`,
|
|
1213
|
+
type: 'text',
|
|
1214
|
+
label: html`<i class="fas fa-file"></i> ${Translate.Render('generic-file-name')}`,
|
|
1215
|
+
containerClass: 'in section-mp input-container-width',
|
|
1216
|
+
placeholder: true,
|
|
1217
|
+
})}
|
|
1218
|
+
</div>
|
|
1219
|
+
<div
|
|
1220
|
+
class="in fll file-explorer-search-col-d"
|
|
1221
|
+
style="display: flex; align-items: center; justify-content: center; height: 100%;"
|
|
1222
|
+
>
|
|
1223
|
+
${await BtnIcon.Render({
|
|
1224
|
+
class: 'in management-table-btn-mini file-explorer-search-submit',
|
|
1225
|
+
label: html`<i class="fas fa-search"></i>`,
|
|
1226
|
+
type: 'button',
|
|
1227
|
+
style: 'top: 10px; margin-right: 5px;',
|
|
1228
|
+
})}
|
|
1229
|
+
${await BtnIcon.Render({
|
|
1230
|
+
class: 'in management-table-btn-mini file-explorer-search-clear',
|
|
1231
|
+
label: html`<i class="fas fa-broom"></i>`,
|
|
1232
|
+
type: 'button',
|
|
1233
|
+
style: 'top: 10px',
|
|
1234
|
+
})}
|
|
1235
|
+
</div>
|
|
1236
|
+
</div>
|
|
517
1237
|
</form>
|
|
518
1238
|
<div class="fl file-explorer-nav">
|
|
519
1239
|
<div class="in fll explorer-file-col">
|
|
@@ -553,8 +1273,12 @@ const FileExplorer = {
|
|
|
553
1273
|
const format = this.documentDataFormat({ document: documentInstance, location });
|
|
554
1274
|
files = format.files;
|
|
555
1275
|
folders = format.folders;
|
|
556
|
-
|
|
1276
|
+
applySearchFilter();
|
|
1277
|
+
currentPage = 0;
|
|
1278
|
+
displayedFiles = getPagedFiles();
|
|
1279
|
+
AgGrid.grids[gridFileId].setGridOption('rowData', displayedFiles);
|
|
557
1280
|
AgGrid.grids[gridFolderId].setGridOption('rowData', folders);
|
|
1281
|
+
updatePaginationUI();
|
|
558
1282
|
},
|
|
559
1283
|
},
|
|
560
1284
|
{
|
|
@@ -588,8 +1312,12 @@ const FileExplorer = {
|
|
|
588
1312
|
const format = this.documentDataFormat({ document: documentInstance, location });
|
|
589
1313
|
files = format.files;
|
|
590
1314
|
folders = format.folders;
|
|
591
|
-
|
|
1315
|
+
applySearchFilter();
|
|
1316
|
+
currentPage = 0;
|
|
1317
|
+
displayedFiles = getPagedFiles();
|
|
1318
|
+
AgGrid.grids[gridFileId].setGridOption('rowData', displayedFiles);
|
|
592
1319
|
AgGrid.grids[gridFolderId].setGridOption('rowData', folders);
|
|
1320
|
+
updatePaginationUI();
|
|
593
1321
|
}
|
|
594
1322
|
},
|
|
595
1323
|
},
|
|
@@ -616,13 +1344,37 @@ const FileExplorer = {
|
|
|
616
1344
|
// rowData: files,
|
|
617
1345
|
rowData: undefined,
|
|
618
1346
|
columnDefs: [
|
|
619
|
-
{ field: 'name', flex: 2, headerName: '
|
|
620
|
-
{ field: '
|
|
621
|
-
{
|
|
1347
|
+
{ field: 'name', flex: 2, headerName: 'Title', cellRenderer: LoadFileNameRenderer },
|
|
1348
|
+
{ field: 'mdFileName', flex: 1, headerName: 'MD File Name' },
|
|
1349
|
+
{ field: 'fileName', flex: 1, headerName: 'Generic File Name' },
|
|
1350
|
+
{ headerName: '', width: 150, cellRenderer: LoadFileActionsRenderer },
|
|
622
1351
|
],
|
|
623
1352
|
},
|
|
624
1353
|
})}
|
|
625
1354
|
</div>
|
|
1355
|
+
<div class="fl file-explorer-pagination" style="padding: 5px 0;">
|
|
1356
|
+
<div class="in fll" style="width: 33.33%;">
|
|
1357
|
+
${await BtnIcon.Render({
|
|
1358
|
+
class: 'in wfa file-explorer-prev-btn',
|
|
1359
|
+
label: html`<i class="fa-solid fa-chevron-left"></i> ${Translate.Render('previous')}`,
|
|
1360
|
+
type: 'button',
|
|
1361
|
+
})}
|
|
1362
|
+
</div>
|
|
1363
|
+
<div class="in fll" style="width: 33.33%; text-align: center;">
|
|
1364
|
+
<span
|
|
1365
|
+
class="file-explorer-pagination-info"
|
|
1366
|
+
style="display: inline-block; padding: 8px 0; min-width: 100px;"
|
|
1367
|
+
>1 / 1 (0 files)</span
|
|
1368
|
+
>
|
|
1369
|
+
</div>
|
|
1370
|
+
<div class="in fll" style="width: 33.33%;">
|
|
1371
|
+
${await BtnIcon.Render({
|
|
1372
|
+
class: 'in wfa file-explorer-next-btn',
|
|
1373
|
+
label: html`${Translate.Render('next')} <i class="fa-solid fa-chevron-right"></i>`,
|
|
1374
|
+
type: 'button',
|
|
1375
|
+
})}
|
|
1376
|
+
</div>
|
|
1377
|
+
</div>
|
|
626
1378
|
</div>
|
|
627
1379
|
</div>
|
|
628
1380
|
</div>
|
|
@@ -665,21 +1417,21 @@ const FileExplorer = {
|
|
|
665
1417
|
if (f.location !== '/' && f.location[f.location.length - 1] === '/') f.location = f.location.slice(0, -1);
|
|
666
1418
|
return f.location;
|
|
667
1419
|
},
|
|
668
|
-
documentDataFormat: function ({ document, location }) {
|
|
1420
|
+
documentDataFormat: function ({ document, location, searchFilters }) {
|
|
669
1421
|
let files = document.map((f) => {
|
|
670
|
-
if (!f.fileId)
|
|
671
|
-
f.fileId = {
|
|
672
|
-
name: f.title + '.md',
|
|
673
|
-
mimetype: 'text/markdown',
|
|
674
|
-
_id: f.mdFileId,
|
|
675
|
-
};
|
|
676
1422
|
return {
|
|
677
1423
|
location: this.locationFormat({ f }),
|
|
678
|
-
name: f.
|
|
679
|
-
|
|
680
|
-
|
|
1424
|
+
name: f.title,
|
|
1425
|
+
mdFileName: f.mdFileId?.name || '',
|
|
1426
|
+
fileName: f.fileId?.name || '',
|
|
1427
|
+
// Use the actual file ID for operations (prefer generic file, fallback to md file)
|
|
1428
|
+
fileId: f.fileId?._id || f.mdFileId?._id || null,
|
|
681
1429
|
_id: f._id,
|
|
682
1430
|
title: f.title,
|
|
1431
|
+
isPublic: f.isPublic || false,
|
|
1432
|
+
// Track file existence for edit form
|
|
1433
|
+
hasMdFile: !!f.mdFileId,
|
|
1434
|
+
hasGenericFile: !!f.fileId,
|
|
683
1435
|
};
|
|
684
1436
|
});
|
|
685
1437
|
let documentId = document._id;
|
|
@@ -692,14 +1444,26 @@ const FileExplorer = {
|
|
|
692
1444
|
locationId: `loc-${i}`,
|
|
693
1445
|
};
|
|
694
1446
|
});
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
1447
|
+
|
|
1448
|
+
const isSearching = searchFilters && (searchFilters.title || searchFilters.mdFile || searchFilters.file);
|
|
1449
|
+
|
|
1450
|
+
if (!isSearching) {
|
|
1451
|
+
files = files.filter((f) => f.location === location);
|
|
1452
|
+
folders = folders
|
|
1453
|
+
.filter((f) => f.location.startsWith(location))
|
|
1454
|
+
.map((f) => {
|
|
1455
|
+
f.fileCount = document.filter((file) => file.location === f.location).length;
|
|
1456
|
+
return f;
|
|
1457
|
+
})
|
|
1458
|
+
.filter((f) => f.fileCount > 0);
|
|
1459
|
+
} else {
|
|
1460
|
+
folders = folders
|
|
1461
|
+
.map((f) => {
|
|
1462
|
+
f.fileCount = files.filter((file) => file.location === f.location).length;
|
|
1463
|
+
return f;
|
|
1464
|
+
})
|
|
1465
|
+
.filter((f) => f.fileCount > 0);
|
|
1466
|
+
}
|
|
703
1467
|
return { files, documentId, folders };
|
|
704
1468
|
},
|
|
705
1469
|
};
|