underpost 2.8.872 → 2.8.874
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/.env.development +2 -1
- package/.env.production +2 -1
- package/.env.test +2 -1
- package/.github/workflows/ghpkg.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +1 -1
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/.github/workflows/release.cd.yml +2 -4
- package/README.md +24 -2
- package/bin/build.js +4 -0
- package/bin/deploy.js +4 -0
- package/cli.md +5 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +174 -0
- package/manifests/deployment/dd-test-development/proxy.yaml +51 -0
- package/package.json +5 -1
- package/src/api/core/core.router.js +2 -1
- package/src/api/default/default.controller.js +6 -1
- package/src/api/default/default.router.js +6 -2
- package/src/api/default/default.service.js +10 -1
- package/src/api/file/file.router.js +2 -1
- package/src/api/test/test.router.js +1 -1
- package/src/api/user/postman_collection.json +216 -0
- package/src/api/user/user.controller.js +25 -60
- package/src/api/user/user.model.js +29 -7
- package/src/api/user/user.router.js +9 -4
- package/src/api/user/user.service.js +84 -32
- package/src/cli/baremetal.js +33 -3
- package/src/cli/cloud-init.js +11 -0
- package/src/cli/deploy.js +44 -22
- package/src/cli/index.js +2 -0
- package/src/cli/lxd.js +7 -0
- package/src/cli/run.js +17 -5
- package/src/cli/ssh.js +20 -6
- package/src/client/components/core/AgGrid.js +28 -6
- package/src/client/components/core/Auth.js +99 -55
- package/src/client/components/core/LogIn.js +16 -23
- package/src/client/components/core/LogOut.js +5 -1
- package/src/client/components/core/Pagination.js +202 -9
- package/src/client/components/core/Router.js +30 -3
- package/src/client/components/core/SignUp.js +1 -2
- package/src/client/components/default/LogInDefault.js +0 -6
- package/src/client/components/default/LogOutDefault.js +0 -16
- package/src/client/services/core/core.service.js +5 -1
- package/src/client/services/default/default.management.js +115 -18
- package/src/client/services/default/default.service.js +9 -4
- package/src/client/services/user/user.management.js +6 -0
- package/src/client/services/user/user.service.js +11 -4
- package/src/index.js +24 -2
- package/src/runtime/lampp/Lampp.js +89 -2
- package/src/runtime/xampp/Xampp.js +48 -1
- package/src/server/auth.js +518 -155
- package/src/server/conf.js +19 -1
- package/src/server/runtime.js +64 -228
- package/src/server/ssr.js +85 -0
- package/src/server/valkey.js +2 -1
|
@@ -8,12 +8,14 @@ import { SignUp } from './SignUp.js';
|
|
|
8
8
|
import { Translate } from './Translate.js';
|
|
9
9
|
import { s } from './VanillaJs.js';
|
|
10
10
|
|
|
11
|
-
const logger = loggerFactory(import.meta);
|
|
11
|
+
const logger = loggerFactory(import.meta, { trace: true });
|
|
12
12
|
|
|
13
13
|
const token = Symbol('token');
|
|
14
14
|
|
|
15
15
|
const guestToken = Symbol('guestToken');
|
|
16
16
|
|
|
17
|
+
const refreshTimeout = Symbol('refreshTimeout');
|
|
18
|
+
|
|
17
19
|
const Auth = {
|
|
18
20
|
[token]: '',
|
|
19
21
|
[guestToken]: '',
|
|
@@ -35,50 +37,65 @@ const Auth = {
|
|
|
35
37
|
getGuestToken: function () {
|
|
36
38
|
return this[guestToken];
|
|
37
39
|
},
|
|
38
|
-
// jwt
|
|
39
40
|
getJWT: function () {
|
|
40
|
-
return `Bearer ${
|
|
41
|
+
if (Auth.getToken()) return `Bearer ${Auth.getToken()}`;
|
|
42
|
+
if (Auth.getGuestToken()) return `Bearer ${Auth.getGuestToken()}`;
|
|
43
|
+
return '';
|
|
41
44
|
},
|
|
42
|
-
|
|
43
|
-
result = {
|
|
44
|
-
data: {
|
|
45
|
-
token: '',
|
|
46
|
-
user: null,
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
) {
|
|
45
|
+
decodeJwt: function (token) {
|
|
50
46
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
} catch (error) {
|
|
55
|
-
logger.error(error);
|
|
56
|
-
localStorage.removeItem('jwt');
|
|
47
|
+
return JSON.parse(atob(token.split('.')[1]));
|
|
48
|
+
} catch (e) {
|
|
49
|
+
return null;
|
|
57
50
|
}
|
|
58
51
|
},
|
|
52
|
+
scheduleTokenRefresh: function () {
|
|
53
|
+
if (this[refreshTimeout]) {
|
|
54
|
+
clearTimeout(this[refreshTimeout]);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const currentToken = Auth.getToken();
|
|
58
|
+
if (!currentToken) return;
|
|
59
|
+
|
|
60
|
+
const payload = Auth.decodeJwt(currentToken);
|
|
61
|
+
if (!payload || !payload.refreshExpiresAt) return;
|
|
62
|
+
|
|
63
|
+
const expiresIn = payload.refreshExpiresAt - Date.now();
|
|
64
|
+
const refreshBuffer = 2 * 60 * 1000; // 2 minutes
|
|
65
|
+
const refreshIn = expiresIn - refreshBuffer;
|
|
66
|
+
|
|
67
|
+
logger.info(`Token refresh in ${refreshIn / (1000 * 60)} minutes`);
|
|
68
|
+
|
|
69
|
+
if (refreshIn <= 0) return; // Already expired or close to it
|
|
70
|
+
|
|
71
|
+
this[refreshTimeout] = setTimeout(async () => {
|
|
72
|
+
const { data, status } = await UserService.get({ id: 'auth' });
|
|
73
|
+
if (status === 'success') {
|
|
74
|
+
logger.info('Refreshed access token.');
|
|
75
|
+
Auth.setToken(data.token);
|
|
76
|
+
localStorage.setItem('jwt', data.token);
|
|
77
|
+
Auth.scheduleTokenRefresh();
|
|
78
|
+
} else Auth.sessionOut();
|
|
79
|
+
}, refreshIn);
|
|
80
|
+
},
|
|
59
81
|
sessionIn: async function (userServicePayload) {
|
|
60
82
|
try {
|
|
61
83
|
const token = userServicePayload?.data?.token ? userServicePayload.data.token : localStorage.getItem('jwt');
|
|
62
|
-
|
|
63
84
|
if (token) {
|
|
64
|
-
|
|
85
|
+
Auth.setToken(token);
|
|
86
|
+
|
|
65
87
|
const result = userServicePayload
|
|
66
|
-
? userServicePayload
|
|
67
|
-
: await (
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
status: _result.status,
|
|
71
|
-
message: _result.message,
|
|
72
|
-
data: {
|
|
73
|
-
user: _result.data,
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
})();
|
|
88
|
+
? userServicePayload // From login/signup
|
|
89
|
+
: await UserService.get({ id: 'auth' });
|
|
90
|
+
|
|
77
91
|
const { status, data, message } = result;
|
|
78
92
|
if (status === 'success') {
|
|
93
|
+
Auth.setToken(data.token);
|
|
79
94
|
localStorage.setItem('jwt', token);
|
|
95
|
+
Auth.renderSessionUI();
|
|
80
96
|
await LogIn.Trigger({ user: data.user });
|
|
81
97
|
await Account.updateForm(data.user);
|
|
98
|
+
Auth.scheduleTokenRefresh();
|
|
82
99
|
return { user: data.user };
|
|
83
100
|
}
|
|
84
101
|
if (message && message.match('expired'))
|
|
@@ -89,41 +106,68 @@ const Auth = {
|
|
|
89
106
|
status: 'warning',
|
|
90
107
|
});
|
|
91
108
|
});
|
|
92
|
-
return await Auth.sessionOut();
|
|
93
109
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
this.deleteToken();
|
|
110
|
+
// Important delete session token if guest token already exists
|
|
111
|
+
Auth.deleteToken();
|
|
97
112
|
localStorage.removeItem('jwt');
|
|
98
|
-
let guestToken = localStorage.getItem('jwt.g');
|
|
99
113
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
guestToken
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
localStorage.removeItem('jwt.g');
|
|
111
|
-
return await Auth.sessionOut();
|
|
112
|
-
} else throw new Error(message);
|
|
114
|
+
// Anon guest session
|
|
115
|
+
let guestToken = localStorage.getItem('jwt.g');
|
|
116
|
+
if (guestToken) {
|
|
117
|
+
Auth.setGuestToken(guestToken);
|
|
118
|
+
let { data, status, message } = await UserService.get({ id: 'auth' });
|
|
119
|
+
if (status === 'success') {
|
|
120
|
+
await LogIn.Trigger(data);
|
|
121
|
+
await Account.updateForm(data.user);
|
|
122
|
+
return data;
|
|
123
|
+
} else logger.error(message);
|
|
113
124
|
}
|
|
114
|
-
await
|
|
115
|
-
return { user: data };
|
|
125
|
+
return await Auth.sessionOut();
|
|
116
126
|
} catch (error) {
|
|
117
127
|
logger.error(error);
|
|
118
128
|
return { user: UserMock.default };
|
|
119
129
|
}
|
|
120
130
|
},
|
|
121
131
|
sessionOut: async function () {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
132
|
+
{
|
|
133
|
+
const result = await UserService.delete({ id: 'logout' });
|
|
134
|
+
localStorage.removeItem('jwt');
|
|
135
|
+
Auth.deleteToken();
|
|
136
|
+
if (this[refreshTimeout]) {
|
|
137
|
+
clearTimeout(this[refreshTimeout]);
|
|
138
|
+
}
|
|
139
|
+
Auth.renderGuestUi();
|
|
140
|
+
LogIn.Scope.user.main.model.user = {};
|
|
141
|
+
await LogOut.Trigger(result);
|
|
142
|
+
}
|
|
143
|
+
{
|
|
144
|
+
localStorage.removeItem('jwt.g');
|
|
145
|
+
Auth.deleteGuestToken();
|
|
146
|
+
const result = await UserService.post({ id: 'guest' });
|
|
147
|
+
localStorage.setItem('jwt.g', result.data.token);
|
|
148
|
+
Auth.setGuestToken(result.data.token);
|
|
149
|
+
return await Auth.sessionIn();
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
renderSessionUI: function () {
|
|
153
|
+
s(`.main-btn-log-in`).style.display = 'none';
|
|
154
|
+
s(`.main-btn-sign-up`).style.display = 'none';
|
|
155
|
+
s(`.main-btn-log-out`).style.display = null;
|
|
156
|
+
s(`.main-btn-account`).style.display = null;
|
|
157
|
+
setTimeout(() => {
|
|
158
|
+
if (s(`.modal-log-in`)) s(`.btn-close-modal-log-in`).click();
|
|
159
|
+
if (s(`.modal-sign-up`)) s(`.btn-close-modal-sign-up`).click();
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
renderGuestUi: function () {
|
|
163
|
+
s(`.main-btn-log-in`).style.display = null;
|
|
164
|
+
s(`.main-btn-sign-up`).style.display = null;
|
|
165
|
+
s(`.main-btn-log-out`).style.display = 'none';
|
|
166
|
+
s(`.main-btn-account`).style.display = 'none';
|
|
167
|
+
setTimeout(() => {
|
|
168
|
+
if (s(`.modal-log-out`)) s(`.btn-close-modal-log-out`).click();
|
|
169
|
+
if (s(`.modal-account`)) s(`.btn-close-modal-account`).click();
|
|
170
|
+
});
|
|
127
171
|
},
|
|
128
172
|
};
|
|
129
173
|
|
|
@@ -24,8 +24,9 @@ const LogIn = {
|
|
|
24
24
|
Event: {},
|
|
25
25
|
Trigger: async function (options) {
|
|
26
26
|
const { user } = options;
|
|
27
|
-
await Webhook.register({ user });
|
|
28
27
|
for (const eventKey of Object.keys(this.Event)) await this.Event[eventKey](options);
|
|
28
|
+
if (!user || user.role === 'guest') return;
|
|
29
|
+
await Webhook.register({ user });
|
|
29
30
|
if (s(`.session`))
|
|
30
31
|
htmls(
|
|
31
32
|
`.session`,
|
|
@@ -72,19 +73,18 @@ const LogIn = {
|
|
|
72
73
|
imageSrc,
|
|
73
74
|
};
|
|
74
75
|
}
|
|
76
|
+
htmls(
|
|
77
|
+
`.action-btn-profile-log-in-render`,
|
|
78
|
+
html`<div class="abs center top-box-profile-img-container">
|
|
79
|
+
<img
|
|
80
|
+
class="abs center top-box-profile-img"
|
|
81
|
+
${this.Scope.user.main.model.user.profileImage
|
|
82
|
+
? `src="${this.Scope.user.main.model.user.profileImage.imageSrc}"`
|
|
83
|
+
: ``}
|
|
84
|
+
/>
|
|
85
|
+
</div>`,
|
|
86
|
+
);
|
|
75
87
|
}
|
|
76
|
-
|
|
77
|
-
htmls(
|
|
78
|
-
`.action-btn-profile-log-in-render`,
|
|
79
|
-
html`<div class="abs center top-box-profile-img-container">
|
|
80
|
-
<img
|
|
81
|
-
class="abs center top-box-profile-img"
|
|
82
|
-
${this.Scope.user.main.model.user.profileImage
|
|
83
|
-
? `src="${this.Scope.user.main.model.user.profileImage.imageSrc}"`
|
|
84
|
-
: ``}
|
|
85
|
-
/>
|
|
86
|
-
</div>`,
|
|
87
|
-
);
|
|
88
88
|
},
|
|
89
89
|
Render: async function () {
|
|
90
90
|
setTimeout(async () => {
|
|
@@ -103,13 +103,7 @@ const LogIn = {
|
|
|
103
103
|
if ('model' in inputData) body[inputData.model] = s(`.${inputData.id}`).value;
|
|
104
104
|
}
|
|
105
105
|
const result = await UserService.post({ id: 'auth', body });
|
|
106
|
-
|
|
107
|
-
await Auth.sessionIn(result);
|
|
108
|
-
setTimeout(() => {
|
|
109
|
-
if (s(`.modal-log-in`)) s(`.btn-close-modal-log-in`).click();
|
|
110
|
-
if (s(`.modal-sign-up`)) s(`.btn-close-modal-sign-up`).click();
|
|
111
|
-
});
|
|
112
|
-
}
|
|
106
|
+
|
|
113
107
|
if (result.status === 'error' && result.message.match('attempts')) {
|
|
114
108
|
htmls(`.login-attempt-warn-value`, result.message.split(':')[1]);
|
|
115
109
|
s(`.login-attempt-warn-container`).classList.remove('hide');
|
|
@@ -119,6 +113,8 @@ const LogIn = {
|
|
|
119
113
|
htmls(`.login-attempt-warn-value0`, result.message.split(':')[1]);
|
|
120
114
|
s(`.login-attempt-warn-container0`).classList.remove('hide');
|
|
121
115
|
} else s(`.login-attempt-warn-container0`).classList.add('hide');
|
|
116
|
+
|
|
117
|
+
if (result.status === 'success') await Auth.sessionIn(result);
|
|
122
118
|
NotificationManager.Push({
|
|
123
119
|
html: result.status === 'success' ? Translate.Render(`${result.status}-user-log-in`) : result.message,
|
|
124
120
|
status: result.status,
|
|
@@ -189,9 +185,6 @@ const LogIn = {
|
|
|
189
185
|
</form>
|
|
190
186
|
`;
|
|
191
187
|
},
|
|
192
|
-
cleanMainUser: () => {
|
|
193
|
-
LogIn.Scope.user.main.model.user = {};
|
|
194
|
-
},
|
|
195
188
|
};
|
|
196
189
|
|
|
197
190
|
export { LogIn };
|
|
@@ -4,11 +4,11 @@ import { LogIn } from './LogIn.js';
|
|
|
4
4
|
import { Translate } from './Translate.js';
|
|
5
5
|
import { htmls, s } from './VanillaJs.js';
|
|
6
6
|
import { Webhook } from './Webhook.js';
|
|
7
|
+
import { NotificationManager } from './NotificationManager.js';
|
|
7
8
|
|
|
8
9
|
const LogOut = {
|
|
9
10
|
Event: {},
|
|
10
11
|
Trigger: async function (options) {
|
|
11
|
-
LogIn.cleanMainUser();
|
|
12
12
|
await Webhook.unregister();
|
|
13
13
|
for (const eventKey of Object.keys(this.Event)) await this.Event[eventKey](options);
|
|
14
14
|
if (s(`.session`))
|
|
@@ -41,6 +41,10 @@ const LogOut = {
|
|
|
41
41
|
s('.btn-log-out').onclick = async (e) => {
|
|
42
42
|
e.preventDefault();
|
|
43
43
|
await Auth.sessionOut();
|
|
44
|
+
NotificationManager.Push({
|
|
45
|
+
html: Translate.Render(`success-logout`),
|
|
46
|
+
status: 'success',
|
|
47
|
+
});
|
|
44
48
|
};
|
|
45
49
|
});
|
|
46
50
|
// Translate.Render('confirm-logout')
|
|
@@ -1,14 +1,207 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { getQueryParams, setQueryParams } from './Router.js';
|
|
2
|
+
|
|
3
|
+
class AgPagination extends HTMLElement {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
this.attachShadow({ mode: 'open' });
|
|
7
|
+
this._gridId = null;
|
|
8
|
+
const queryParams = getQueryParams();
|
|
9
|
+
this._currentPage = parseInt(queryParams.page, 10) || 1;
|
|
10
|
+
this._limit = parseInt(queryParams.limit, 10) || 10;
|
|
11
|
+
this._totalPages = 1;
|
|
12
|
+
this._totalItems = 0;
|
|
13
|
+
this.handlePageChange = this.handlePageChange.bind(this);
|
|
14
|
+
this.handleLimitChange = this.handleLimitChange.bind(this);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static get observedAttributes() {
|
|
18
|
+
return ['grid-id', 'current-page', 'total-pages', 'total-items', 'limit'];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
22
|
+
switch (name) {
|
|
23
|
+
case 'grid-id':
|
|
24
|
+
this._gridId = newValue;
|
|
25
|
+
break;
|
|
26
|
+
case 'current-page':
|
|
27
|
+
this._currentPage = parseInt(newValue, 10) || this._currentPage;
|
|
28
|
+
break;
|
|
29
|
+
case 'total-pages':
|
|
30
|
+
this._totalPages = parseInt(newValue, 10) || 1;
|
|
31
|
+
break;
|
|
32
|
+
case 'total-items':
|
|
33
|
+
this._totalItems = parseInt(newValue, 10) || 0;
|
|
34
|
+
break;
|
|
35
|
+
case 'limit':
|
|
36
|
+
this._limit = parseInt(newValue, 10) || this._limit;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
this.update();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
connectedCallback() {
|
|
43
|
+
this.render();
|
|
44
|
+
this.addEventListeners();
|
|
45
|
+
this.update();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
disconnectedCallback() {
|
|
49
|
+
// Event listeners on shadow DOM are garbage collected with the component
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
handlePageChange(newPage) {
|
|
53
|
+
if (newPage < 1 || newPage > this._totalPages || newPage === this._currentPage) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
this._currentPage = newPage;
|
|
57
|
+
setQueryParams({ page: newPage, limit: this._limit });
|
|
58
|
+
this.dispatchEvent(new CustomEvent('page-change', { detail: { page: newPage } }));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
handleLimitChange(event) {
|
|
62
|
+
const newLimit = parseInt(event.target.value, 10);
|
|
63
|
+
if (newLimit === this._limit) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this._limit = newLimit;
|
|
67
|
+
this._currentPage = 1; // Reset to first page on limit change
|
|
68
|
+
setQueryParams({ page: 1, limit: newLimit });
|
|
69
|
+
this.dispatchEvent(new CustomEvent('limit-change', { detail: { limit: newLimit } }));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
update() {
|
|
73
|
+
if (!this.shadowRoot.querySelector('#page-info')) return;
|
|
74
|
+
|
|
75
|
+
const isFirstPage = this._currentPage === 1;
|
|
76
|
+
const isLastPage = this._currentPage === this._totalPages;
|
|
77
|
+
|
|
78
|
+
this.shadowRoot.querySelector('#first-page').disabled = isFirstPage;
|
|
79
|
+
this.shadowRoot.querySelector('#prev-page').disabled = isFirstPage;
|
|
80
|
+
this.shadowRoot.querySelector('#next-page').disabled = isLastPage;
|
|
81
|
+
this.shadowRoot.querySelector('#last-page').disabled = isLastPage;
|
|
82
|
+
|
|
83
|
+
const startItem = this._totalItems > 0 ? (this._currentPage - 1) * this._limit + 1 : 0;
|
|
84
|
+
const endItem = Math.min(this._currentPage * this._limit, this._totalItems);
|
|
85
|
+
|
|
86
|
+
this.shadowRoot.querySelector('#summary-info').textContent = `${startItem} - ${endItem} of ${this._totalItems}`;
|
|
87
|
+
this.shadowRoot.querySelector('#page-info').textContent = `Page ${this._currentPage} of ${this._totalPages}`;
|
|
88
|
+
|
|
89
|
+
const limitSelector = this.shadowRoot.querySelector('#limit-selector');
|
|
90
|
+
if (limitSelector.value != this._limit) {
|
|
91
|
+
limitSelector.value = this._limit;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.renderPageButtons();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
renderPageButtons() {
|
|
98
|
+
const pageButtonsContainer = this.shadowRoot.querySelector('#page-buttons');
|
|
99
|
+
pageButtonsContainer.innerHTML = '';
|
|
100
|
+
|
|
101
|
+
const maxButtons = 5;
|
|
102
|
+
let startPage = Math.max(1, this._currentPage - Math.floor(maxButtons / 2));
|
|
103
|
+
let endPage = Math.min(this._totalPages, startPage + maxButtons - 1);
|
|
104
|
+
|
|
105
|
+
if (endPage - startPage + 1 < maxButtons) {
|
|
106
|
+
startPage = Math.max(1, endPage - maxButtons + 1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (let i = startPage; i <= endPage; i++) {
|
|
110
|
+
const button = document.createElement('button');
|
|
111
|
+
button.textContent = i;
|
|
112
|
+
button.disabled = i === this._currentPage;
|
|
113
|
+
if (i === this._currentPage) {
|
|
114
|
+
button.classList.add('active');
|
|
115
|
+
}
|
|
116
|
+
button.addEventListener('click', () => this.handlePageChange(i));
|
|
117
|
+
pageButtonsContainer.appendChild(button);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
addEventListeners() {
|
|
122
|
+
this.shadowRoot.querySelector('#first-page').addEventListener('click', () => this.handlePageChange(1));
|
|
123
|
+
this.shadowRoot
|
|
124
|
+
.querySelector('#prev-page')
|
|
125
|
+
.addEventListener('click', () => this.handlePageChange(this._currentPage - 1));
|
|
126
|
+
this.shadowRoot
|
|
127
|
+
.querySelector('#next-page')
|
|
128
|
+
.addEventListener('click', () => this.handlePageChange(this._currentPage + 1));
|
|
129
|
+
this.shadowRoot
|
|
130
|
+
.querySelector('#last-page')
|
|
131
|
+
.addEventListener('click', () => this.handlePageChange(this._totalPages));
|
|
132
|
+
this.shadowRoot.querySelector('#limit-selector').addEventListener('change', this.handleLimitChange);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
render() {
|
|
136
|
+
this.shadowRoot.innerHTML = html`
|
|
4
137
|
<style>
|
|
5
|
-
|
|
138
|
+
:host {
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
justify-content: center;
|
|
142
|
+
padding: 8px;
|
|
143
|
+
font-family: sans-serif;
|
|
144
|
+
font-size: 14px;
|
|
145
|
+
gap: 8px;
|
|
146
|
+
}
|
|
147
|
+
button {
|
|
148
|
+
border: 1px solid #ccc;
|
|
149
|
+
background-color: #f0f0f0;
|
|
150
|
+
padding: 6px 12px;
|
|
151
|
+
cursor: pointer;
|
|
152
|
+
border-radius: 4px;
|
|
153
|
+
}
|
|
154
|
+
button:disabled {
|
|
155
|
+
cursor: not-allowed;
|
|
156
|
+
opacity: 0.5;
|
|
157
|
+
}
|
|
158
|
+
button.active {
|
|
159
|
+
border-color: #007bff;
|
|
160
|
+
background-color: #007bff;
|
|
161
|
+
color: white;
|
|
162
|
+
}
|
|
163
|
+
#page-info {
|
|
164
|
+
min-width: 80px;
|
|
165
|
+
text-align: center;
|
|
166
|
+
}
|
|
167
|
+
#page-buttons {
|
|
168
|
+
display: flex;
|
|
169
|
+
gap: 4px;
|
|
170
|
+
}
|
|
171
|
+
.summary-panel,
|
|
172
|
+
.page-summary-panel {
|
|
173
|
+
display: flex;
|
|
174
|
+
align-items: center;
|
|
175
|
+
gap: 8px;
|
|
176
|
+
}
|
|
177
|
+
#limit-selector {
|
|
178
|
+
border: 1px solid #ccc;
|
|
179
|
+
background-color: #f0f0f0;
|
|
180
|
+
padding: 6px 12px;
|
|
181
|
+
border-radius: 4px;
|
|
6
182
|
}
|
|
7
183
|
</style>
|
|
8
|
-
|
|
9
|
-
|
|
184
|
+
<div class="summary-panel">
|
|
185
|
+
<span id="summary-info"></span>
|
|
186
|
+
</div>
|
|
187
|
+
<button id="first-page">First</button>
|
|
188
|
+
<button id="prev-page">Previous</button>
|
|
189
|
+
<div class="page-summary-panel">
|
|
190
|
+
<div id="page-buttons"></div>
|
|
191
|
+
<span id="page-info"></span>
|
|
192
|
+
</div>
|
|
193
|
+
<button id="next-page">Next</button>
|
|
194
|
+
<button id="last-page">Last</button>
|
|
195
|
+
<select id="limit-selector">
|
|
196
|
+
<option value="10">10</option>
|
|
197
|
+
<option value="20">20</option>
|
|
198
|
+
<option value="50">50</option>
|
|
199
|
+
<option value="100">100</option>
|
|
200
|
+
</select>
|
|
10
201
|
`;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
customElements.define('ag-pagination', AgPagination);
|
|
13
206
|
|
|
14
|
-
export {
|
|
207
|
+
export { AgPagination };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Router module for handling routing in a PWA application.
|
|
3
3
|
* @module src/client/components/core/Router.js
|
|
4
4
|
* @namespace PwaRouter
|
|
5
5
|
*/
|
|
@@ -245,7 +245,8 @@ const closeModalRouteChangeEvent = (options = {}) => {
|
|
|
245
245
|
|
|
246
246
|
for (const event of Object.keys(closeModalRouteChangeEvents)) closeModalRouteChangeEvents[event]();
|
|
247
247
|
if (topModalId) Modal.setTopModalCallback(topModalId);
|
|
248
|
-
setPath(`${getProxyPath()}${Modal.Data[topModalId]?.options?.route
|
|
248
|
+
setPath(`${getProxyPath()}${Modal.Data[topModalId]?.options?.route ?? ''}`);
|
|
249
|
+
setDocTitle(Modal.Data[topModalId]?.options?.route ?? '');
|
|
249
250
|
};
|
|
250
251
|
|
|
251
252
|
/**
|
|
@@ -255,7 +256,7 @@ const closeModalRouteChangeEvent = (options = {}) => {
|
|
|
255
256
|
* @param {string} options.route - The route associated with the modal view.
|
|
256
257
|
* @memberof PwaRouter
|
|
257
258
|
*/
|
|
258
|
-
const handleModalViewRoute = (options = { route: '
|
|
259
|
+
const handleModalViewRoute = (options = { route: '' }) => {
|
|
259
260
|
const { route } = options;
|
|
260
261
|
if (!route) return;
|
|
261
262
|
|
|
@@ -270,6 +271,31 @@ const handleModalViewRoute = (options = { route: 'home' }) => {
|
|
|
270
271
|
}
|
|
271
272
|
};
|
|
272
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Sets or updates query parameters in the URL.
|
|
276
|
+
* It preserves the existing path, hash, and other query parameters.
|
|
277
|
+
*
|
|
278
|
+
* @param {Object.<string, string|number>} newParams - An object of query parameters to set or update.
|
|
279
|
+
* If a value is `null` or `undefined`, the parameter will be removed.
|
|
280
|
+
* @param {object} [options={ replace: true }] - Options for history manipulation.
|
|
281
|
+
* @param {boolean} [options.replace=true] - If true, uses `history.replaceState` instead of `history.pushState`.
|
|
282
|
+
* @memberof PwaRouter
|
|
283
|
+
*/
|
|
284
|
+
const setQueryParams = (newParams, options = { replace: true }) => {
|
|
285
|
+
const url = new URL(window.location.href);
|
|
286
|
+
Object.entries(newParams).forEach(([key, value]) => {
|
|
287
|
+
if (value === null || value === undefined) {
|
|
288
|
+
url.searchParams.delete(key);
|
|
289
|
+
} else {
|
|
290
|
+
url.searchParams.set(key, value);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const newPath = url.pathname + url.search + url.hash;
|
|
295
|
+
|
|
296
|
+
history.pushState(history.state, '', newPath);
|
|
297
|
+
};
|
|
298
|
+
|
|
273
299
|
export {
|
|
274
300
|
RouterEvents,
|
|
275
301
|
closeModalRouteChangeEvents,
|
|
@@ -284,4 +310,5 @@ export {
|
|
|
284
310
|
getQueryParams,
|
|
285
311
|
getProxyPath,
|
|
286
312
|
setPath,
|
|
313
|
+
setQueryParams,
|
|
287
314
|
};
|
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
import { UserService } from '../../services/user/user.service.js';
|
|
2
1
|
import { Auth } from '../core/Auth.js';
|
|
3
2
|
import { LogIn } from '../core/LogIn.js';
|
|
4
|
-
import { s } from '../core/VanillaJs.js';
|
|
5
3
|
import { ElementsDefault } from './ElementsDefault.js';
|
|
6
4
|
|
|
7
5
|
const LogInDefault = async function () {
|
|
8
6
|
LogIn.Event['LogInDefault'] = async (options) => {
|
|
9
7
|
const { token, user } = options;
|
|
10
8
|
ElementsDefault.Data.user.main.model.user = user;
|
|
11
|
-
s(`.main-btn-log-in`).style.display = 'none';
|
|
12
|
-
s(`.main-btn-sign-up`).style.display = 'none';
|
|
13
|
-
s(`.main-btn-log-out`).style.display = null;
|
|
14
|
-
s(`.main-btn-account`).style.display = null;
|
|
15
9
|
};
|
|
16
10
|
const { user } = await Auth.sessionIn();
|
|
17
11
|
ElementsDefault.Data.user.main.model.user = user;
|
|
@@ -1,25 +1,9 @@
|
|
|
1
|
-
import { Auth } from '../core/Auth.js';
|
|
2
1
|
import { LogOut } from '../core/LogOut.js';
|
|
3
|
-
import { NotificationManager } from '../core/NotificationManager.js';
|
|
4
|
-
import { Translate } from '../core/Translate.js';
|
|
5
|
-
import { s } from '../core/VanillaJs.js';
|
|
6
2
|
import { ElementsDefault } from './ElementsDefault.js';
|
|
7
3
|
|
|
8
4
|
const LogOutDefault = async function () {
|
|
9
5
|
LogOut.Event['LogOutDefault'] = async (result = { user: { _id: '' } }) => {
|
|
10
6
|
ElementsDefault.Data.user.main.model.user = result.user;
|
|
11
|
-
|
|
12
|
-
s(`.main-btn-log-out`).style.display = 'none';
|
|
13
|
-
s(`.main-btn-account`).style.display = 'none';
|
|
14
|
-
s(`.main-btn-log-in`).style.display = null;
|
|
15
|
-
s(`.main-btn-sign-up`).style.display = null;
|
|
16
|
-
if (s(`.modal-log-out`)) s(`.btn-close-modal-log-out`).click();
|
|
17
|
-
if (s(`.modal-account`)) s(`.btn-close-modal-account`).click();
|
|
18
|
-
|
|
19
|
-
NotificationManager.Push({
|
|
20
|
-
html: Translate.Render(`success-logout`),
|
|
21
|
-
status: 'success',
|
|
22
|
-
});
|
|
23
7
|
};
|
|
24
8
|
};
|
|
25
9
|
|
|
@@ -33,7 +33,11 @@ const getWsBaseUrl = (options = { id: '', endpoint: '', wsBasePath: '' }) =>
|
|
|
33
33
|
}${options?.endpoint ? options.endpoint : ''}${options?.id ? `/${options.id}` : ''}`;
|
|
34
34
|
|
|
35
35
|
const headersFactory = (headerId = '') => {
|
|
36
|
-
const headers = {
|
|
36
|
+
const headers = {
|
|
37
|
+
Authorization: Auth.getJWT(),
|
|
38
|
+
withCredentials: true,
|
|
39
|
+
credentials: 'include', // no cors: 'same-origin'
|
|
40
|
+
};
|
|
37
41
|
switch (headerId) {
|
|
38
42
|
case 'file':
|
|
39
43
|
return headers;
|