@underpostnet/underpost 2.97.0 → 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.
- package/README.md +2 -2
- package/baremetal/commission-workflows.json +33 -3
- package/bin/deploy.js +1 -1
- package/cli.md +7 -2
- package/conf.js +3 -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/packer/scripts/fuse-tar-root +3 -3
- package/scripts/disk-clean.sh +23 -23
- package/scripts/gpu-diag.sh +2 -2
- package/scripts/ip-info.sh +11 -11
- package/scripts/maas-upload-boot-resource.sh +1 -1
- package/scripts/nvim.sh +1 -1
- package/scripts/packer-setup.sh +13 -13
- package/scripts/rocky-setup.sh +2 -2
- package/scripts/rpmfusion-ffmpeg-setup.sh +4 -4
- package/scripts/ssl.sh +7 -7
- 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 +30 -1
- package/src/api/document/document.router.js +6 -0
- package/src/api/document/document.service.js +423 -51
- 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/baremetal.js +689 -329
- package/src/cli/cluster.js +50 -52
- package/src/cli/db.js +424 -166
- package/src/cli/deploy.js +1 -1
- package/src/cli/index.js +12 -1
- package/src/cli/lxd.js +3 -3
- package/src/cli/repository.js +1 -1
- package/src/cli/run.js +2 -1
- 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 +9 -3
- package/src/client/components/core/Chat.js +2 -2
- package/src/client/components/core/Content.js +159 -78
- package/src/client/components/core/Css.js +16 -2
- package/src/client/components/core/CssCore.js +16 -12
- package/src/client/components/core/FileExplorer.js +115 -8
- package/src/client/components/core/Input.js +204 -11
- package/src/client/components/core/LogIn.js +42 -20
- package/src/client/components/core/Modal.js +257 -177
- package/src/client/components/core/Panel.js +324 -27
- package/src/client/components/core/PanelForm.js +280 -73
- package/src/client/components/core/PublicProfile.js +888 -0
- package/src/client/components/core/Router.js +117 -15
- package/src/client/components/core/SearchBox.js +1117 -0
- 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 +98 -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 +451 -64
- package/src/client/services/default/default.service.js +13 -6
- package/src/client/services/document/document.service.js +23 -0
- package/src/client/services/file/file.service.js +43 -16
- package/src/client/services/user/user.service.js +13 -9
- 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/dns.js +4 -4
- package/src/server/object-layer.js +0 -3
- package/src/ws/IoInterface.js +2 -2
|
@@ -7,7 +7,7 @@ import { loggerFactory } from '../../components/core/Logger.js';
|
|
|
7
7
|
import { Modal } from '../../components/core/Modal.js';
|
|
8
8
|
import { NotificationManager } from '../../components/core/NotificationManager.js';
|
|
9
9
|
import { Translate } from '../../components/core/Translate.js';
|
|
10
|
-
import { getQueryParams, RouterEvents, setQueryParams } from '../../components/core/Router.js';
|
|
10
|
+
import { getQueryParams, listenQueryParamsChange, RouterEvents, setQueryParams } from '../../components/core/Router.js';
|
|
11
11
|
import { s } from '../../components/core/VanillaJs.js';
|
|
12
12
|
import { DefaultService } from './default.service.js';
|
|
13
13
|
|
|
@@ -52,49 +52,136 @@ const columnDefFormatter = (obj, columnDefs, customFormat) => {
|
|
|
52
52
|
|
|
53
53
|
const DefaultManagement = {
|
|
54
54
|
Tokens: {},
|
|
55
|
-
loadTable: async function (id, options = { reload: true, force: true }) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
55
|
+
loadTable: async function (id, options = { reload: true, force: true, createHistory: false, skipUrlUpdate: false }) {
|
|
56
|
+
try {
|
|
57
|
+
const { serviceId, columnDefs, customFormat, gridId } = this.Tokens[id];
|
|
58
|
+
|
|
59
|
+
let _page = this.Tokens[id].page;
|
|
60
|
+
let _limit = this.Tokens[id].limit;
|
|
61
|
+
let _id = this.Tokens[id].serviceOptions?.get?.id ?? undefined;
|
|
62
|
+
|
|
63
|
+
let filterModel = this.Tokens[id].filterModel || {};
|
|
64
|
+
let sortModel = this.Tokens[id].sortModel || [];
|
|
65
|
+
|
|
66
|
+
const gridApi = this.Tokens[id].gridApi || AgGrid.grids[gridId];
|
|
67
|
+
|
|
68
|
+
if (gridApi) {
|
|
69
|
+
filterModel = gridApi.getFilterModel() || {};
|
|
70
|
+
const columnState = gridApi.getColumnState();
|
|
71
|
+
if (columnState) {
|
|
72
|
+
sortModel = columnState
|
|
73
|
+
.filter((col) => col.sort)
|
|
74
|
+
.map((col) => ({ colId: col.colId, sort: col.sort, sortIndex: col.sortIndex }))
|
|
75
|
+
.sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0));
|
|
76
|
+
}
|
|
69
77
|
}
|
|
70
|
-
}
|
|
71
|
-
this.Tokens[id].lastOptions = {
|
|
72
|
-
page: _page,
|
|
73
|
-
limit: _limit,
|
|
74
|
-
id: _id,
|
|
75
|
-
};
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
79
|
+
// Clean up filterModel and sortModel for URL params
|
|
80
|
+
const filterModelStr = Object.keys(filterModel).length > 0 ? JSON.stringify(filterModel) : null;
|
|
81
|
+
const sortModelStr = sortModel.length > 0 ? JSON.stringify(sortModel) : null;
|
|
82
|
+
|
|
83
|
+
// Update URL parameters to reflect current grid state
|
|
84
|
+
// Use pushState (createHistory) for filter/sort changes to enable browser back/forward
|
|
85
|
+
// Skip URL update when handling browser navigation to avoid interfering with history
|
|
86
|
+
if (!options.skipUrlUpdate) {
|
|
87
|
+
setQueryParams(
|
|
88
|
+
{
|
|
89
|
+
page: _page,
|
|
90
|
+
limit: _limit,
|
|
91
|
+
filterModel: filterModelStr,
|
|
92
|
+
sortModel: sortModelStr,
|
|
93
|
+
},
|
|
94
|
+
{ replace: !options.createHistory },
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!options.force && this.Tokens[id].lastOptions) {
|
|
99
|
+
const last = this.Tokens[id].lastOptions;
|
|
100
|
+
if (
|
|
101
|
+
_page === last.page &&
|
|
102
|
+
_limit === last.limit &&
|
|
103
|
+
_id === last.id &&
|
|
104
|
+
JSON.stringify(filterModel) === JSON.stringify(last.filterModel) &&
|
|
105
|
+
JSON.stringify(sortModel) === JSON.stringify(last.sortModel)
|
|
106
|
+
) {
|
|
107
|
+
logger.warn(`DefaultManagement loadTable ${serviceId} - Skipping load, options unchanged`);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
this.Tokens[id].lastOptions = {
|
|
112
|
+
page: _page,
|
|
113
|
+
limit: _limit,
|
|
114
|
+
id: _id,
|
|
115
|
+
filterModel,
|
|
116
|
+
sortModel,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Update tokens with current state
|
|
120
|
+
this.Tokens[id].filterModel = filterModel;
|
|
121
|
+
this.Tokens[id].sortModel = sortModel;
|
|
122
|
+
|
|
123
|
+
const queryOptions = {
|
|
124
|
+
page: _page,
|
|
125
|
+
limit: _limit,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
if (_id) {
|
|
129
|
+
queryOptions.id = _id;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (filterModel && Object.keys(filterModel).length > 0) {
|
|
133
|
+
queryOptions.filterModel = filterModel;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (sortModel && sortModel.length > 0) {
|
|
137
|
+
queryOptions.sortModel = sortModel;
|
|
138
|
+
// Legacy simple sort support
|
|
139
|
+
if (sortModel.length === 1) {
|
|
140
|
+
queryOptions.sort = sortModel[0].colId;
|
|
141
|
+
queryOptions.asc = sortModel[0].sort === 'asc' ? '1' : '0';
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
logger.info(`Loading table ${serviceId}`, {
|
|
146
|
+
page: _page,
|
|
147
|
+
limit: _limit,
|
|
148
|
+
hasFilters: Object.keys(filterModel).length > 0,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const result = await this.Tokens[id].ServiceProvider.get(queryOptions);
|
|
152
|
+
if (result.status === 'success') {
|
|
153
|
+
const { data, total, page, totalPages } = result.data;
|
|
154
|
+
this.Tokens[id].total = total;
|
|
155
|
+
this.Tokens[id].page = page;
|
|
156
|
+
this.Tokens[id].totalPages = totalPages;
|
|
157
|
+
const rowDataScope = data.map((row) => columnDefFormatter(row, columnDefs, customFormat));
|
|
158
|
+
if (options.reload) {
|
|
159
|
+
const grid = AgGrid.grids[this.Tokens[id].gridId];
|
|
160
|
+
if (grid && grid.setGridOption) {
|
|
161
|
+
grid.setGridOption('rowData', rowDataScope);
|
|
162
|
+
} else {
|
|
163
|
+
logger.warn(`Grid ${gridId} not found or not ready for setGridOption`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const paginationComp = s(`#ag-pagination-${this.Tokens[id].gridId}`);
|
|
167
|
+
if (paginationComp) {
|
|
168
|
+
paginationComp.setAttribute('current-page', this.Tokens[id].page);
|
|
169
|
+
paginationComp.setAttribute('total-pages', this.Tokens[id].totalPages);
|
|
170
|
+
paginationComp.setAttribute('total-items', this.Tokens[id].total);
|
|
171
|
+
} else {
|
|
172
|
+
logger.warn(`Pagination component not found for grid ${gridId}`);
|
|
173
|
+
}
|
|
174
|
+
setTimeout(async () => {
|
|
175
|
+
if (DefaultManagement.Tokens[id].readyRowDataEvent)
|
|
176
|
+
for (const event of Object.keys(DefaultManagement.Tokens[id].readyRowDataEvent))
|
|
177
|
+
await DefaultManagement.Tokens[id].readyRowDataEvent[event](rowDataScope);
|
|
178
|
+
}, 1);
|
|
179
|
+
} else {
|
|
180
|
+
logger.error(`Failed to load table ${serviceId}:`, result);
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
logger.error(`Error in loadTable for ${id}:`, error);
|
|
184
|
+
throw error;
|
|
98
185
|
}
|
|
99
186
|
},
|
|
100
187
|
refreshTable: async function (id) {
|
|
@@ -115,13 +202,100 @@ const DefaultManagement = {
|
|
|
115
202
|
logger.info('DefaultManagement RenderTable', options);
|
|
116
203
|
const id = options?.idModal ? options.idModal : getId(this.Tokens, `${serviceId}-`);
|
|
117
204
|
const gridId = `${serviceId}-grid-${id}`;
|
|
205
|
+
const queryParamsListenerId = `default-management-${id}`;
|
|
118
206
|
const queryParams = getQueryParams();
|
|
119
207
|
const page = parseInt(queryParams.page) || 1;
|
|
120
208
|
const defaultLimit = paginationOptions?.limitOptions?.[0] || 10;
|
|
121
209
|
const limit = parseInt(queryParams.limit) || defaultLimit;
|
|
122
|
-
this.Tokens[id] = { ...this.Tokens[id], ...options, gridId, page, limit, total: 0, totalPages: 1 };
|
|
123
210
|
|
|
124
|
-
|
|
211
|
+
let filterModel = {};
|
|
212
|
+
let sortModel = [];
|
|
213
|
+
try {
|
|
214
|
+
if (queryParams.filterModel) filterModel = JSON.parse(queryParams.filterModel);
|
|
215
|
+
if (queryParams.sortModel) sortModel = JSON.parse(queryParams.sortModel);
|
|
216
|
+
} catch (e) {
|
|
217
|
+
logger.warn('Error parsing filter/sort model from URL', e);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Enhance column definitions for Date filtering and ensure colId
|
|
221
|
+
const enhancedColumnDefs = columnDefs.map((col) => {
|
|
222
|
+
const enhancedCol = {
|
|
223
|
+
...col,
|
|
224
|
+
colId: col.field, // Ensure colId matches field
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
if (enhancedCol.cellDataType === 'date' || enhancedCol.filter === 'agDateColumnFilter') {
|
|
228
|
+
enhancedCol.filter = 'agDateColumnFilter';
|
|
229
|
+
|
|
230
|
+
// Value getter to ensure date is properly parsed
|
|
231
|
+
if (!enhancedCol.valueGetter) {
|
|
232
|
+
enhancedCol.valueGetter = (params) => {
|
|
233
|
+
const value = params.data?.[enhancedCol.field];
|
|
234
|
+
if (!value) return null;
|
|
235
|
+
const date = new Date(value);
|
|
236
|
+
return isNaN(date.getTime()) ? null : date;
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Value formatter for display
|
|
241
|
+
if (!enhancedCol.valueFormatter) {
|
|
242
|
+
enhancedCol.valueFormatter = (params) => {
|
|
243
|
+
if (!params.value) return '';
|
|
244
|
+
const date = new Date(params.value);
|
|
245
|
+
if (isNaN(date.getTime())) return '';
|
|
246
|
+
return date.toLocaleDateString('en-US', {
|
|
247
|
+
year: 'numeric',
|
|
248
|
+
month: '2-digit',
|
|
249
|
+
day: '2-digit',
|
|
250
|
+
});
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
enhancedCol.filterParams = {
|
|
255
|
+
comparator: (filterLocalDateAtMidnight, cellValue) => {
|
|
256
|
+
if (cellValue == null) return -1;
|
|
257
|
+
const cellDate = new Date(cellValue);
|
|
258
|
+
if (isNaN(cellDate.getTime())) return -1;
|
|
259
|
+
// Compare dates (ignoring time)
|
|
260
|
+
const cellTime = new Date(cellDate).setHours(0, 0, 0, 0);
|
|
261
|
+
const filterTime = filterLocalDateAtMidnight.getTime();
|
|
262
|
+
if (filterTime === cellTime) return 0;
|
|
263
|
+
if (cellTime < filterTime) return -1;
|
|
264
|
+
if (cellTime > filterTime) return 1;
|
|
265
|
+
return 0;
|
|
266
|
+
},
|
|
267
|
+
browserDatePicker: true,
|
|
268
|
+
minValidYear: 2000,
|
|
269
|
+
maxValidYear: 2100,
|
|
270
|
+
inRangeInclusive: true,
|
|
271
|
+
debounceMs: 500,
|
|
272
|
+
...enhancedCol.filterParams,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
return enhancedCol;
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
this.Tokens[id] = {
|
|
279
|
+
...this.Tokens[id],
|
|
280
|
+
...options,
|
|
281
|
+
columnDefs: enhancedColumnDefs, // Use enhanced definitions
|
|
282
|
+
gridId,
|
|
283
|
+
page,
|
|
284
|
+
limit,
|
|
285
|
+
total: 0,
|
|
286
|
+
totalPages: 1,
|
|
287
|
+
filterModel,
|
|
288
|
+
sortModel,
|
|
289
|
+
isInitializing: true, // Flag to prevent double loading during grid ready
|
|
290
|
+
isProcessingQueryChange: false, // Flag to prevent listener recursion
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
setQueryParams({
|
|
294
|
+
page,
|
|
295
|
+
limit,
|
|
296
|
+
filterModel: Object.keys(filterModel).length > 0 ? JSON.stringify(filterModel) : null,
|
|
297
|
+
sortModel: sortModel.length > 0 ? JSON.stringify(sortModel) : null,
|
|
298
|
+
});
|
|
125
299
|
setTimeout(async () => {
|
|
126
300
|
// https://www.ag-grid.com/javascript-data-grid/data-update-transactions/
|
|
127
301
|
|
|
@@ -203,7 +377,7 @@ const DefaultManagement = {
|
|
|
203
377
|
|
|
204
378
|
AgGrid.grids[gridId].setGridOption(
|
|
205
379
|
'columnDefs',
|
|
206
|
-
|
|
380
|
+
enhancedColumnDefs.concat(
|
|
207
381
|
permissions.remove
|
|
208
382
|
? [
|
|
209
383
|
{
|
|
@@ -217,7 +391,7 @@ const DefaultManagement = {
|
|
|
217
391
|
: [],
|
|
218
392
|
),
|
|
219
393
|
);
|
|
220
|
-
|
|
394
|
+
// Initial loadTable is now called in onGridReady after grid is fully initialized
|
|
221
395
|
// {
|
|
222
396
|
// const result = await ServiceProvider.get();
|
|
223
397
|
// if (result.status === 'success') {
|
|
@@ -348,6 +522,110 @@ const DefaultManagement = {
|
|
|
348
522
|
DefaultManagement.loadTable(id);
|
|
349
523
|
}
|
|
350
524
|
});
|
|
525
|
+
|
|
526
|
+
// Listen to query parameter changes for browser back/forward navigation
|
|
527
|
+
listenQueryParamsChange({
|
|
528
|
+
id: queryParamsListenerId,
|
|
529
|
+
event: (queryParams) => {
|
|
530
|
+
// Prevent recursion - if we're already processing a query change, skip
|
|
531
|
+
if (this.Tokens[id].isProcessingQueryChange) {
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const newPage = parseInt(queryParams.page, 10) || 1;
|
|
536
|
+
const newLimit = parseInt(queryParams.limit, 10) || this.Tokens[id].limit || 10;
|
|
537
|
+
const newFilterModel = queryParams.filterModel;
|
|
538
|
+
const newSortModel = queryParams.sortModel;
|
|
539
|
+
|
|
540
|
+
let shouldReload = false;
|
|
541
|
+
|
|
542
|
+
// Check if page or limit changed
|
|
543
|
+
if (newPage !== this.Tokens[id].page || newLimit !== this.Tokens[id].limit) {
|
|
544
|
+
this.Tokens[id].page = newPage;
|
|
545
|
+
this.Tokens[id].limit = newLimit;
|
|
546
|
+
shouldReload = true;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Check if filter or sort changed by comparing with actual grid state
|
|
550
|
+
const gridApi = AgGrid.grids[gridId];
|
|
551
|
+
let filterChanged = false;
|
|
552
|
+
let sortChanged = false;
|
|
553
|
+
|
|
554
|
+
if (gridApi) {
|
|
555
|
+
// Get current grid filter state
|
|
556
|
+
const currentGridFilterModel = gridApi.getFilterModel() || {};
|
|
557
|
+
const currentGridFilterStr = JSON.stringify(currentGridFilterModel);
|
|
558
|
+
const newFilterStr = newFilterModel || '{}';
|
|
559
|
+
|
|
560
|
+
// Get current grid sort state
|
|
561
|
+
const currentColumnState = gridApi.getColumnState() || [];
|
|
562
|
+
const currentGridSortModel = currentColumnState
|
|
563
|
+
.filter((col) => col.sort)
|
|
564
|
+
.map((col) => ({ colId: col.colId, sort: col.sort, sortIndex: col.sortIndex }))
|
|
565
|
+
.sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0));
|
|
566
|
+
const currentGridSortStr = JSON.stringify(currentGridSortModel);
|
|
567
|
+
const newSortStr = newSortModel || '[]';
|
|
568
|
+
|
|
569
|
+
filterChanged = currentGridFilterStr !== newFilterStr;
|
|
570
|
+
sortChanged = currentGridSortStr !== newSortStr;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (filterChanged || sortChanged) {
|
|
574
|
+
// Parse and apply the new filter/sort models
|
|
575
|
+
try {
|
|
576
|
+
this.Tokens[id].filterModel = newFilterModel ? JSON.parse(newFilterModel) : {};
|
|
577
|
+
} catch (e) {
|
|
578
|
+
this.Tokens[id].filterModel = {};
|
|
579
|
+
}
|
|
580
|
+
try {
|
|
581
|
+
this.Tokens[id].sortModel = newSortModel ? JSON.parse(newSortModel) : [];
|
|
582
|
+
} catch (e) {
|
|
583
|
+
this.Tokens[id].sortModel = [];
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Apply filters and sorts to the grid
|
|
587
|
+
if (gridApi) {
|
|
588
|
+
// Temporarily disable filter/sort change handlers to prevent recursion
|
|
589
|
+
this.Tokens[id].isProcessingQueryChange = true;
|
|
590
|
+
|
|
591
|
+
if (filterChanged) {
|
|
592
|
+
gridApi.setFilterModel(this.Tokens[id].filterModel);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (sortChanged) {
|
|
596
|
+
// Apply sort model
|
|
597
|
+
const columnState = this.Tokens[id].sortModel.map((sortItem) => ({
|
|
598
|
+
colId: sortItem.colId,
|
|
599
|
+
sort: sortItem.sort,
|
|
600
|
+
sortIndex: sortItem.sortIndex,
|
|
601
|
+
}));
|
|
602
|
+
if (columnState.length > 0) {
|
|
603
|
+
gridApi.applyColumnState({
|
|
604
|
+
state: columnState,
|
|
605
|
+
defaultState: { sort: null },
|
|
606
|
+
});
|
|
607
|
+
} else {
|
|
608
|
+
gridApi.applyColumnState({
|
|
609
|
+
defaultState: { sort: null },
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Re-enable handlers after a short delay
|
|
615
|
+
setTimeout(() => {
|
|
616
|
+
this.Tokens[id].isProcessingQueryChange = false;
|
|
617
|
+
}, 100);
|
|
618
|
+
}
|
|
619
|
+
shouldReload = true;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (shouldReload) {
|
|
623
|
+
// Skip URL update since browser already changed it (back/forward navigation)
|
|
624
|
+
DefaultManagement.loadTable(id, { reload: true, force: true, createHistory: false, skipUrlUpdate: true });
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
});
|
|
628
|
+
|
|
351
629
|
EventsUI.onClick(`.management-table-btn-reload-${id}`, async () => {
|
|
352
630
|
try {
|
|
353
631
|
// Reload data from server
|
|
@@ -371,26 +649,75 @@ const DefaultManagement = {
|
|
|
371
649
|
s(`#ag-pagination-${gridId}`).addEventListener('page-change', async (event) => {
|
|
372
650
|
const token = DefaultManagement.Tokens[id];
|
|
373
651
|
token.page = event.detail.page;
|
|
374
|
-
|
|
652
|
+
// Skip URL update since Pagination component already updated it
|
|
653
|
+
await DefaultManagement.loadTable(id, { skipUrlUpdate: true });
|
|
375
654
|
});
|
|
376
655
|
s(`#ag-pagination-${gridId}`).addEventListener('limit-change', async (event) => {
|
|
377
656
|
const token = DefaultManagement.Tokens[id];
|
|
378
657
|
token.limit = event.detail.limit;
|
|
379
658
|
token.page = 1; // Reset to first page
|
|
380
|
-
|
|
659
|
+
// Skip URL update since Pagination component already updated it
|
|
660
|
+
await DefaultManagement.loadTable(id, { skipUrlUpdate: true });
|
|
381
661
|
});
|
|
382
662
|
RouterEvents[id] = async (...args) => {
|
|
383
663
|
const queryParams = getQueryParams();
|
|
384
664
|
const page = parseInt(queryParams.page) || 1;
|
|
385
665
|
const limit = parseInt(queryParams.limit) || 10;
|
|
666
|
+
let filterModel = {};
|
|
667
|
+
let sortModel = [];
|
|
668
|
+
try {
|
|
669
|
+
if (queryParams.filterModel) filterModel = JSON.parse(queryParams.filterModel);
|
|
670
|
+
if (queryParams.sortModel) sortModel = JSON.parse(queryParams.sortModel);
|
|
671
|
+
} catch (e) {}
|
|
672
|
+
|
|
386
673
|
const token = DefaultManagement.Tokens[id];
|
|
674
|
+
if (!token) {
|
|
675
|
+
// Token doesn't exist yet, table hasn't been initialized
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Check if state in URL is different from current state
|
|
680
|
+
const isPaginationChanged = token.page !== page || token.limit !== limit;
|
|
681
|
+
const isFilterChanged = JSON.stringify(token.filterModel || {}) !== JSON.stringify(filterModel);
|
|
682
|
+
const isSortChanged = JSON.stringify(token.sortModel || []) !== JSON.stringify(sortModel);
|
|
387
683
|
|
|
388
|
-
|
|
389
|
-
if (token.page !== page || token.limit !== limit) {
|
|
684
|
+
if (isPaginationChanged || isFilterChanged || isSortChanged) {
|
|
390
685
|
token.page = page;
|
|
391
686
|
token.limit = limit;
|
|
392
|
-
|
|
393
|
-
|
|
687
|
+
token.filterModel = filterModel;
|
|
688
|
+
token.sortModel = sortModel;
|
|
689
|
+
|
|
690
|
+
// If grid is active, we should update its state to match URL
|
|
691
|
+
const gridApi = AgGrid.grids[gridId];
|
|
692
|
+
if (gridApi && gridApi.setFilterModel && gridApi.applyColumnState) {
|
|
693
|
+
// Updating filter/sort on grid will trigger onFilterChanged/onSortChanged -> loadTable
|
|
694
|
+
// But we must ensure it happens.
|
|
695
|
+
if (isFilterChanged) {
|
|
696
|
+
try {
|
|
697
|
+
gridApi.setFilterModel(filterModel);
|
|
698
|
+
} catch (e) {
|
|
699
|
+
console.warn('Failed to set filter model:', e);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
if (isSortChanged) {
|
|
703
|
+
try {
|
|
704
|
+
gridApi.applyColumnState({
|
|
705
|
+
state: sortModel,
|
|
706
|
+
defaultState: { sort: null },
|
|
707
|
+
});
|
|
708
|
+
} catch (e) {
|
|
709
|
+
console.warn('Failed to apply column state:', e);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
// If only pagination changed, we must load manually because no grid event triggers
|
|
713
|
+
if (isPaginationChanged && !isFilterChanged && !isSortChanged) {
|
|
714
|
+
await DefaultManagement.loadTable(id);
|
|
715
|
+
}
|
|
716
|
+
} else if (!gridApi) {
|
|
717
|
+
// Grid doesn't exist yet, just load the table data when it's ready
|
|
718
|
+
// The onGridReady event will handle applying the initial state
|
|
719
|
+
logger.warn(`Grid not ready for ${gridId}, state will be applied on grid ready`);
|
|
720
|
+
}
|
|
394
721
|
}
|
|
395
722
|
};
|
|
396
723
|
}, 1);
|
|
@@ -444,19 +771,69 @@ const DefaultManagement = {
|
|
|
444
771
|
filter: true,
|
|
445
772
|
autoHeight: true,
|
|
446
773
|
},
|
|
447
|
-
|
|
774
|
+
onGridReady: (params) => {
|
|
775
|
+
this.Tokens[id].gridApi = params.api;
|
|
776
|
+
// Apply initial state from URL
|
|
777
|
+
const { filterModel, sortModel } = this.Tokens[id];
|
|
778
|
+
if (filterModel && Object.keys(filterModel).length > 0) {
|
|
779
|
+
params.api.setFilterModel(filterModel);
|
|
780
|
+
}
|
|
781
|
+
if (sortModel && sortModel.length > 0) {
|
|
782
|
+
params.api.applyColumnState({
|
|
783
|
+
state: sortModel,
|
|
784
|
+
defaultState: { sort: null },
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
// Load initial data now that grid is ready
|
|
788
|
+
// Filter/sort state has been applied, this will fetch data from server
|
|
789
|
+
DefaultManagement.loadTable(id).finally(() => {
|
|
790
|
+
// Mark initialization complete to allow future filter/sort events
|
|
791
|
+
this.Tokens[id].isInitializing = false;
|
|
792
|
+
});
|
|
793
|
+
},
|
|
794
|
+
onFilterChanged: () => {
|
|
795
|
+
// Skip if still initializing (state being applied in onGridReady)
|
|
796
|
+
if (this.Tokens[id].isInitializing) return;
|
|
797
|
+
// Skip if we're processing a query change from browser navigation
|
|
798
|
+
if (this.Tokens[id].isProcessingQueryChange) return;
|
|
799
|
+
// Reset to page 1 on filter change
|
|
800
|
+
this.Tokens[id].page = 1;
|
|
801
|
+
// Create history entry for filter changes
|
|
802
|
+
DefaultManagement.loadTable(id, { reload: true, force: true, createHistory: true });
|
|
803
|
+
},
|
|
804
|
+
onSortChanged: () => {
|
|
805
|
+
// Skip if still initializing (state being applied in onGridReady)
|
|
806
|
+
if (this.Tokens[id].isInitializing) return;
|
|
807
|
+
// Skip if we're processing a query change from browser navigation
|
|
808
|
+
if (this.Tokens[id].isProcessingQueryChange) return;
|
|
809
|
+
// Create history entry for sort changes
|
|
810
|
+
DefaultManagement.loadTable(id, { reload: true, force: true, createHistory: true });
|
|
811
|
+
},
|
|
812
|
+
editType: 'fullRow', // Keep fullRow for add new row, but cells will auto-save
|
|
448
813
|
// rowData: [],
|
|
449
814
|
onCellValueChanged: async (...args) => {
|
|
450
|
-
|
|
451
|
-
//
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
815
|
+
const [event] = args;
|
|
816
|
+
// Only auto-save for existing rows (with _id), not new rows
|
|
817
|
+
if (event.data && event.data._id) {
|
|
818
|
+
logger.info('onCellValueChanged - auto-saving', args);
|
|
819
|
+
const body = event.data ? event.data : {};
|
|
820
|
+
const result = await ServiceProvider.put({ id: event.data._id, body });
|
|
821
|
+
NotificationManager.Push({
|
|
822
|
+
html: result.status === 'error' ? result.message : `${Translate.Render('success-update-item')}`,
|
|
823
|
+
status: result.status,
|
|
824
|
+
});
|
|
825
|
+
if (result.status === 'success') {
|
|
826
|
+
AgGrid.grids[gridId].applyTransaction({
|
|
827
|
+
update: [event.data],
|
|
828
|
+
});
|
|
829
|
+
// Restore default buttons after save
|
|
830
|
+
s(`.management-table-btn-save-${id}`).classList.add('hide');
|
|
831
|
+
if (permissions.add) s(`.management-table-btn-add-${id}`).classList.remove('hide');
|
|
832
|
+
if (permissions.remove) s(`.management-table-btn-clean-${id}`).classList.remove('hide');
|
|
833
|
+
if (permissions.reload) s(`.management-table-btn-reload-${id}`).classList.remove('hide');
|
|
834
|
+
DefaultManagement.loadTable(id, { reload: false });
|
|
835
|
+
}
|
|
836
|
+
}
|
|
460
837
|
},
|
|
461
838
|
rowSelection: 'single',
|
|
462
839
|
onSelectionChanged: async (...args) => {
|
|
@@ -465,8 +842,18 @@ const DefaultManagement = {
|
|
|
465
842
|
const selectedRows = AgGrid.grids[gridId].getSelectedRows();
|
|
466
843
|
logger.info('selectedRows', selectedRows);
|
|
467
844
|
},
|
|
845
|
+
onCellEditingStarted: async (...args) => {
|
|
846
|
+
const [event] = args;
|
|
847
|
+
// Only show save button for new rows (without _id)
|
|
848
|
+
if (event.data && !event.data._id) {
|
|
849
|
+
s(`.management-table-btn-save-${id}`).classList.remove('hide');
|
|
850
|
+
if (permissions.add) s(`.management-table-btn-add-${id}`).classList.add('hide');
|
|
851
|
+
if (permissions.remove) s(`.management-table-btn-clean-${id}`).classList.add('hide');
|
|
852
|
+
if (permissions.reload) s(`.management-table-btn-reload-${id}`).classList.add('hide');
|
|
853
|
+
}
|
|
854
|
+
},
|
|
468
855
|
onRowEditingStarted: async (...args) => {
|
|
469
|
-
// Show only save button when editing starts
|
|
856
|
+
// Show only save button when editing starts (for new rows)
|
|
470
857
|
s(`.management-table-btn-save-${id}`).classList.remove('hide');
|
|
471
858
|
if (permissions.add) s(`.management-table-btn-add-${id}`).classList.add('hide');
|
|
472
859
|
if (permissions.remove) s(`.management-table-btn-clean-${id}`).classList.add('hide');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Auth } from '../../components/core/Auth.js';
|
|
2
2
|
import { loggerFactory } from '../../components/core/Logger.js';
|
|
3
|
-
import { getApiBaseUrl, headersFactory, payloadFactory } from '../core/core.service.js';
|
|
3
|
+
import { getApiBaseUrl, headersFactory, payloadFactory, buildQueryUrl } from '../core/core.service.js';
|
|
4
4
|
|
|
5
5
|
const logger = loggerFactory(import.meta);
|
|
6
6
|
|
|
@@ -49,11 +49,18 @@ const DefaultService = {
|
|
|
49
49
|
return reject(error);
|
|
50
50
|
}),
|
|
51
51
|
),
|
|
52
|
-
get: (options = {
|
|
53
|
-
const { id, page, limit } = options;
|
|
54
|
-
const url =
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
get: (options = {}) => {
|
|
53
|
+
const { id, page, limit, filterModel, sortModel, sort, asc, order } = options;
|
|
54
|
+
const url = buildQueryUrl(getApiBaseUrl({ id, endpoint }), {
|
|
55
|
+
page,
|
|
56
|
+
limit,
|
|
57
|
+
filterModel,
|
|
58
|
+
sortModel,
|
|
59
|
+
sort,
|
|
60
|
+
asc,
|
|
61
|
+
order,
|
|
62
|
+
});
|
|
63
|
+
|
|
57
64
|
return new Promise((resolve, reject) =>
|
|
58
65
|
fetch(url.toString(), {
|
|
59
66
|
method: 'GET',
|
|
@@ -111,6 +111,29 @@ const DocumentService = {
|
|
|
111
111
|
return reject(error);
|
|
112
112
|
}),
|
|
113
113
|
),
|
|
114
|
+
high: (options = { params: {} }) =>
|
|
115
|
+
new Promise((resolve, reject) => {
|
|
116
|
+
const url = new URL(getApiBaseUrl({ id: 'public/high', endpoint }));
|
|
117
|
+
if (options.params) {
|
|
118
|
+
Object.keys(options.params).forEach((key) => url.searchParams.append(key, options.params[key]));
|
|
119
|
+
}
|
|
120
|
+
fetch(url, {
|
|
121
|
+
method: 'GET',
|
|
122
|
+
headers: headersFactory(),
|
|
123
|
+
credentials: 'include',
|
|
124
|
+
})
|
|
125
|
+
.then(async (res) => {
|
|
126
|
+
return await res.json();
|
|
127
|
+
})
|
|
128
|
+
.then((res) => {
|
|
129
|
+
logger.info(res);
|
|
130
|
+
return resolve(res);
|
|
131
|
+
})
|
|
132
|
+
.catch((error) => {
|
|
133
|
+
logger.error(error);
|
|
134
|
+
return reject(error);
|
|
135
|
+
});
|
|
136
|
+
}),
|
|
114
137
|
};
|
|
115
138
|
|
|
116
139
|
export { DocumentService };
|