@sdcorejs/angular 21.0.6 → 21.0.8
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 +8 -2
- package/assets/STYLE-GUIDE.md +13 -0
- package/fesm2022/sdcorejs-angular-components-anchor.mjs +11 -12
- package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +210 -40
- package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-avatar.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-badge.mjs +4 -4
- package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-base.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-button.mjs +4 -4
- package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-chart.mjs +12 -12
- package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-code-editor.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs +11 -12
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-editor.mjs +6 -6
- package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs +203 -203
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-history.mjs +7 -7
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs +10 -10
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-inform.mjs +7 -7
- package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-mini-editor.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +5 -5
- package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-modal.mjs +4 -4
- package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-operator.mjs +4 -4
- package/fesm2022/sdcorejs-angular-components-operator.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-org-chart.mjs +7 -7
- package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-preview.mjs +9 -9
- package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs +31 -31
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-quick-action.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-section.mjs +7 -7
- package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +8 -6
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-splitter.mjs +28 -14
- package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-stepper.mjs +6 -6
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs +18 -18
- package/fesm2022/sdcorejs-angular-components-tab.mjs +6 -6
- package/fesm2022/sdcorejs-angular-components-table.mjs +110 -111
- package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tree.mjs +7 -7
- package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs +36 -31
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-view.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components.mjs +1 -0
- package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-directives.mjs +21 -21
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +6 -7
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +6 -6
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +9 -9
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip.mjs +9 -9
- package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs +11 -12
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date.mjs +8 -9
- package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs +14 -15
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-directives.mjs +12 -12
- package/fesm2022/sdcorejs-angular-forms-inline-text.mjs +4 -4
- package/fesm2022/sdcorejs-angular-forms-inline-text.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs +3 -3
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs +7 -8
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input.mjs +6 -6
- package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-label.mjs +3 -3
- package/fesm2022/sdcorejs-angular-forms-radio.mjs +6 -7
- package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-select.mjs +7 -7
- package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-switch.mjs +6 -7
- package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs +7 -9
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms.mjs +4 -4
- package/fesm2022/sdcorejs-angular-handlers.mjs +3 -3
- package/fesm2022/sdcorejs-angular-i18n.mjs +6 -6
- package/fesm2022/sdcorejs-angular-interceptors.mjs +6 -6
- package/fesm2022/sdcorejs-angular-modules-auth.mjs +9 -9
- package/fesm2022/sdcorejs-angular-modules-authom.mjs +7 -7
- package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +37 -14
- package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-layout.mjs +75 -76
- package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-permission.mjs +9 -9
- package/fesm2022/sdcorejs-angular-pipes.mjs +14 -14
- package/fesm2022/sdcorejs-angular-services-api.mjs +11 -12
- package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-cache.mjs +3 -3
- package/fesm2022/sdcorejs-angular-services-confirm.mjs +8 -8
- package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-docx.mjs +3 -3
- package/fesm2022/sdcorejs-angular-services-excel.mjs +3 -3
- package/fesm2022/sdcorejs-angular-services-license.mjs +3 -3
- package/fesm2022/sdcorejs-angular-services-loading.mjs +3 -3
- package/fesm2022/sdcorejs-angular-services-notify.mjs +9 -9
- package/fesm2022/sdcorejs-angular-services-storage.mjs +3 -3
- package/modules/keycloak/htmls/auth-keycloak-error.html +54 -0
- package/modules/keycloak/htmls/silent-renew.html +7 -0
- package/package.json +1 -1
- package/types/sdcorejs-angular-components-autoid-inspector.d.ts +47 -137
- package/types/sdcorejs-angular-components-upload-file.d.ts +7 -4
- package/types/sdcorejs-angular-components.d.ts +1 -0
- package/types/sdcorejs-angular-forms-input-number.d.ts +0 -1
- package/types/sdcorejs-angular-modules-keycloak.d.ts +30 -0
|
@@ -97,8 +97,8 @@ class ToastComponent {
|
|
|
97
97
|
get dataMessage() {
|
|
98
98
|
return Array.isArray(this.data.message) ? this.data.message.join(' | ') : this.data.message;
|
|
99
99
|
}
|
|
100
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
101
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
100
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ToastComponent, deps: [{ token: SdNotifyService }, { token: i2.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
|
|
101
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: ToastComponent, isStandalone: true, selector: "toast", inputs: { data: "data" }, host: { listeners: { "mouseenter": "pauseTimer()", "mouseleave": "resumeTimer()" }, properties: { "@toastAnimation": "true", "class": "\"bg-white sd-toast\"", "attr.data-autoid": "autoId", "attr.data-type": "data.type", "attr.data-title": "data.title ?? null", "attr.data-message": "dataMessage" } }, ngImport: i0, template: "<div class=\"sd-toast__bar\" [class]=\"'bg-' + data.type\"></div>\n\n<div class=\"sd-toast__icon\" [class]=\"'text-' + data.type\">\n @switch (data.type) {\n @case ('success') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>\n }\n @case ('info') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\"/></svg>\n }\n @case ('warning') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/></svg>\n }\n @case ('error') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>\n }\n }\n</div>\n\n<div class=\"sd-toast__content\">\n <div class=\"sd-toast__header\">\n <strong class=\"sd-toast__title\">{{ data.title || ('core.notify.type.' + data.type | translate) }}</strong>\n <button class=\"sd-toast__close\" (click)=\"close()\">\u2715</button>\n </div>\n\n <div class=\"sd-toast__body\">\n @if (!isMultiMessage) {\n <!-- why: m\u1EB7c \u0111\u1ECBnh render TEXT (auto-escape) \u2014 an to\u00E0n. Ch\u1EC9 render HTML\n (\u0111\u00E3 sanitize) khi data.html=true. -->\n @if (data.html) {\n <div [innerHTML]=\"sanitizeHtml(singleMessage)\"></div>\n } @else {\n <div>{{ singleMessage }}</div>\n }\n }\n @else {\n <ul class=\"msg-list\">\n @for (msg of displayMessages; track msg) {\n @if (data.html) {\n <li [innerHTML]=\"sanitizeHtml('- ' + msg)\"></li>\n } @else {\n <li>{{ '- ' + msg }}</li>\n }\n }\n </ul>\n\n @if (hasMore) {\n <button type=\"button\" \n class=\"toggle-more btn-link\" \n [class]=\"'text-' + data.type\" \n (click)=\"toggleExpand()\">\n {{ isExpanded() ? ('core.notify.show-less' | translate) : ('core.notify.show-more' | translate: { count: restCount }) }}\n </button>\n }\n }\n </div>\n\n @if (data.actionLabel) {\n <div class=\"sd-toast__actions\">\n <button class=\"btn-action\" \n [class]=\"'bg-' + data.type + '-light text-' + data.type\"\n (click)=\"onActionClick()\">\n {{ data.actionLabel }}\n </button>\n </div>\n }\n</div>", styles: ["@charset \"UTF-8\";:host{display:flex;width:350px;border-radius:8px;box-shadow:0 4px 12px #00000026;margin-bottom:12px;overflow:hidden;position:relative;font-family:Roboto,sans-serif;pointer-events:auto}.sd-toast__bar{width:6px;flex-shrink:0}.sd-toast__icon{padding:12px 0 0 12px;flex-shrink:0}.sd-toast__icon svg{width:24px;height:24px}.sd-toast__content{flex-grow:1;padding:12px;display:flex;flex-direction:column}.sd-toast__header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:4px}.sd-toast__title{font-size:16px;font-weight:600}.sd-toast__close{background:none;border:none;font-size:16px;cursor:pointer;line-height:1;padding:0;opacity:.5;transition:opacity .2s}.sd-toast__close:hover{opacity:1}.sd-toast__body{font-size:14px;line-height:1.4;word-break:break-word}.sd-toast__actions{display:flex;justify-content:flex-end;margin-top:8px}.msg-list{padding-left:0;list-style:none;margin:0}.toggle-more{background:none;border:none;padding:0;font:inherit;cursor:pointer;font-size:13px;margin-top:4px;font-weight:500;text-align:left}.toggle-more:hover{text-decoration:underline}.toggle-more:focus-visible{outline:2px solid currentColor;outline-offset:2px}.btn-action{border:none;padding:6px 12px;border-radius:4px;font-weight:600;cursor:pointer;font-size:13px;transition:filter .2s}.btn-action:hover{filter:brightness(.95)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: TranslatePipe, name: "translate" }], animations: [
|
|
102
102
|
trigger('toastAnimation', [
|
|
103
103
|
transition(':enter', [
|
|
104
104
|
style({ transform: 'translateX(100%)', opacity: 0 }),
|
|
@@ -110,7 +110,7 @@ class ToastComponent {
|
|
|
110
110
|
])
|
|
111
111
|
] });
|
|
112
112
|
}
|
|
113
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
113
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ToastComponent, decorators: [{
|
|
114
114
|
type: Component,
|
|
115
115
|
args: [{ selector: 'toast', standalone: true, imports: [CommonModule, TranslatePipe], animations: [
|
|
116
116
|
trigger('toastAnimation', [
|
|
@@ -144,8 +144,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
144
144
|
|
|
145
145
|
class ToastContainerComponent {
|
|
146
146
|
toasts = signal([], ...(ngDevMode ? [{ debugName: "toasts" }] : /* istanbul ignore next */ []));
|
|
147
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
148
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
147
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ToastContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
148
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: ToastContainerComponent, isStandalone: true, selector: "toast-container", ngImport: i0, template: `
|
|
149
149
|
<div class="toast-container">
|
|
150
150
|
@for (toast of toasts(); track toast.id) {
|
|
151
151
|
<toast [data]="toast"> </toast>
|
|
@@ -153,7 +153,7 @@ class ToastContainerComponent {
|
|
|
153
153
|
</div>
|
|
154
154
|
`, isInline: true, styles: [".toast-container{position:fixed;top:20px;right:20px;z-index:9999;display:flex;flex-direction:column;pointer-events:none}sd-toast{pointer-events:auto}\n"], dependencies: [{ kind: "component", type: ToastComponent, selector: "toast", inputs: ["data"] }] });
|
|
155
155
|
}
|
|
156
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
156
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ToastContainerComponent, decorators: [{
|
|
157
157
|
type: Component,
|
|
158
158
|
args: [{ selector: 'toast-container', standalone: true, imports: [ToastComponent], template: `
|
|
159
159
|
<div class="toast-container">
|
|
@@ -278,10 +278,10 @@ class SdNotifyService {
|
|
|
278
278
|
});
|
|
279
279
|
this.#timers = {};
|
|
280
280
|
}
|
|
281
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
282
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
281
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdNotifyService, deps: [{ token: i0.ApplicationRef }, { token: i0.EnvironmentInjector }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
282
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdNotifyService, providedIn: 'root' });
|
|
283
283
|
}
|
|
284
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
284
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdNotifyService, decorators: [{
|
|
285
285
|
type: Injectable,
|
|
286
286
|
args: [{
|
|
287
287
|
providedIn: 'root',
|
|
@@ -151,10 +151,10 @@ class SdStorageService {
|
|
|
151
151
|
return val;
|
|
152
152
|
return JSON.parse(JSON.stringify(val));
|
|
153
153
|
}
|
|
154
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
155
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
154
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdStorageService, deps: [{ token: SD_STORAGE_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
155
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdStorageService, providedIn: 'root' });
|
|
156
156
|
}
|
|
157
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
157
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdStorageService, decorators: [{
|
|
158
158
|
type: Injectable,
|
|
159
159
|
args: [{
|
|
160
160
|
providedIn: 'root',
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="vi">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Lỗi đăng nhập</title>
|
|
7
|
+
<!--
|
|
8
|
+
Trang lỗi tĩnh (fallback) cho luồng đăng nhập Keycloak — đích redirect khi keycloak.init() thất bại.
|
|
9
|
+
|
|
10
|
+
MẪU TỔNG QUÁT TỪ CORE: dùng được NGAY, không cần chỉnh sửa gì. Consumer COPY file này vào
|
|
11
|
+
`public/auth-keycloak-error.html` (hoặc tên theo `authErrorUrl`). Khi muốn, tuỳ biến tại các
|
|
12
|
+
placeholder bên dưới (logo, thông tin hỗ trợ, nội dung, ngôn ngữ).
|
|
13
|
+
|
|
14
|
+
Nguyên tắc: KHÔNG phụ thuộc JS/CSS/ảnh bên ngoài (bundle app có thể đã lỗi) → style để inline,
|
|
15
|
+
mặc định không nhúng tài nguyên nào.
|
|
16
|
+
-->
|
|
17
|
+
<style>
|
|
18
|
+
* { box-sizing: border-box; }
|
|
19
|
+
html, body { height: 100%; margin: 0; }
|
|
20
|
+
body {
|
|
21
|
+
display: flex; align-items: center; justify-content: center;
|
|
22
|
+
min-height: 100vh; padding: 24px;
|
|
23
|
+
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
|
|
24
|
+
color: #1f2937; background: #f5f6f8;
|
|
25
|
+
}
|
|
26
|
+
.box {
|
|
27
|
+
max-width: 480px; width: 100%; text-align: center;
|
|
28
|
+
background: #fff; border: 1px solid #e5e7eb; border-radius: 12px;
|
|
29
|
+
padding: 40px 32px; box-shadow: 0 1px 3px rgba(0, 0, 0, .08);
|
|
30
|
+
}
|
|
31
|
+
h1 { font-size: 20px; margin: 0 0 12px; }
|
|
32
|
+
p { margin: 8px 0; line-height: 1.6; color: #4b5563; font-size: 14px; }
|
|
33
|
+
.actions { margin-top: 24px; }
|
|
34
|
+
.btn {
|
|
35
|
+
display: inline-block; padding: 10px 20px; border-radius: 8px;
|
|
36
|
+
background: #2563eb; color: #fff; text-decoration: none; font-size: 14px;
|
|
37
|
+
}
|
|
38
|
+
</style>
|
|
39
|
+
</head>
|
|
40
|
+
<body>
|
|
41
|
+
<main class="box">
|
|
42
|
+
<!-- (Tuỳ chọn) Logo — bỏ comment và sửa src:
|
|
43
|
+
<img src="/your-logo.svg" alt="logo" style="height: 40px; margin-bottom: 24px;" /> -->
|
|
44
|
+
<h1>Có lỗi xảy ra khi đăng nhập</h1>
|
|
45
|
+
<p>Hệ thống đang gặp sự cố kỹ thuật trong quá trình xác thực. Trang chưa thể tải đầy đủ thông tin.</p>
|
|
46
|
+
<p>Vui lòng thử lại sau ít phút.</p>
|
|
47
|
+
<!-- (Tuỳ chọn) Thông tin hỗ trợ — bỏ comment và sửa email/đường dẫn:
|
|
48
|
+
<p>Cần hỗ trợ? Liên hệ <a href="mailto:you@example.com">you@example.com</a></p> -->
|
|
49
|
+
<div class="actions">
|
|
50
|
+
<a class="btn" href="/">Thử lại</a>
|
|
51
|
+
</div>
|
|
52
|
+
</main>
|
|
53
|
+
</body>
|
|
54
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import * as _angular_core from '@angular/core';
|
|
2
|
-
import { OnDestroy } from '@angular/core';
|
|
2
|
+
import { InjectionToken, OnDestroy } from '@angular/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Runtime state attributes the component renders on the same anchor as `data-autoid`.
|
|
6
6
|
* Every field is OPTIONAL — only present when the component actually renders that attribute.
|
|
7
7
|
* All values are the raw DOM string (already "true"/"false" or stringified number/value).
|
|
8
8
|
*/
|
|
9
|
-
interface
|
|
9
|
+
interface AutoidElementState {
|
|
10
10
|
disabled?: string;
|
|
11
11
|
loading?: string;
|
|
12
12
|
empty?: string;
|
|
13
13
|
invalid?: string;
|
|
14
14
|
opened?: string;
|
|
15
15
|
count?: string;
|
|
16
|
-
/** Serialized DOM `data-value` (NOT `input.value`). Distinct from `
|
|
16
|
+
/** Serialized DOM `data-value` (NOT `input.value`). Distinct from `AutoidElement.text` field. */
|
|
17
17
|
dataValue?: string;
|
|
18
18
|
required?: string;
|
|
19
19
|
maxlength?: string;
|
|
@@ -24,7 +24,7 @@ interface SdAutoidElementState {
|
|
|
24
24
|
title?: string;
|
|
25
25
|
message?: string;
|
|
26
26
|
}
|
|
27
|
-
interface
|
|
27
|
+
interface AutoidElement {
|
|
28
28
|
stt: number;
|
|
29
29
|
name: string;
|
|
30
30
|
autoid: string;
|
|
@@ -38,7 +38,7 @@ interface SdAutoidElement {
|
|
|
38
38
|
xpath: string;
|
|
39
39
|
duplicate: boolean;
|
|
40
40
|
/** Runtime state attributes read from the DOM. Always an object — may be empty `{}`. */
|
|
41
|
-
state:
|
|
41
|
+
state: AutoidElementState;
|
|
42
42
|
/**
|
|
43
43
|
* `data-autoid` of the closest ancestor `<sd-table>` that contains this element,
|
|
44
44
|
* or `undefined` if this element is top-level (not inside any `sd-table`).
|
|
@@ -54,54 +54,75 @@ interface SdAutoidElement {
|
|
|
54
54
|
/** Human-readable warning attached to a fallback element (only set when `missingAutoid`). */
|
|
55
55
|
warning?: string;
|
|
56
56
|
}
|
|
57
|
-
interface
|
|
57
|
+
interface AutoidMissing {
|
|
58
58
|
tag: string;
|
|
59
59
|
selector: string;
|
|
60
60
|
outerHtmlPreview: string;
|
|
61
61
|
nameHint: string;
|
|
62
62
|
}
|
|
63
|
-
interface
|
|
63
|
+
interface AutoidAuditResult {
|
|
64
64
|
total: number;
|
|
65
65
|
duplicates: Record<string, number>;
|
|
66
66
|
duplicateCount: number;
|
|
67
|
-
missing:
|
|
67
|
+
missing: AutoidMissing[];
|
|
68
68
|
missingCount: number;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
interface
|
|
72
|
-
/**
|
|
71
|
+
interface SdAutoidInspectorConfiguration {
|
|
72
|
+
/** Base URL cua backend sinh E2E test, vi du `http://localhost:3012`. */
|
|
73
|
+
host?: string;
|
|
74
|
+
/** Selector phu them can audit (mac dinh component da cover sd-* form + sd-button). */
|
|
73
75
|
extraRequireSelectors?: string[];
|
|
74
|
-
/** Root scan,
|
|
76
|
+
/** Root scan, mac dinh document.body. */
|
|
75
77
|
root?: HTMLElement;
|
|
76
78
|
}
|
|
77
|
-
/**
|
|
78
|
-
declare const
|
|
79
|
+
/** Cau hinh global cho AutoId Inspector. `[config]` tren component co the override theo instance. */
|
|
80
|
+
declare const SD_AUTOID_INSPECTOR_CONFIGURATION: InjectionToken<SdAutoidInspectorConfiguration>;
|
|
81
|
+
|
|
82
|
+
type AutoidE2eTarget = 'playwright' | 'robot';
|
|
83
|
+
interface AutoidRobotExportContext {
|
|
84
|
+
requirement?: string;
|
|
85
|
+
testCases?: string;
|
|
86
|
+
testData?: string;
|
|
87
|
+
precondition?: string;
|
|
88
|
+
component?: string;
|
|
89
|
+
storyLinkages?: string;
|
|
90
|
+
qcNotes?: string;
|
|
91
|
+
}
|
|
79
92
|
|
|
80
93
|
type Segment = 'audit' | 'elements' | 'export';
|
|
94
|
+
type RobotContextField = keyof AutoidRobotExportContext;
|
|
81
95
|
declare class SdAutoidInspector implements OnDestroy {
|
|
82
96
|
#private;
|
|
83
97
|
enabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
84
|
-
config: _angular_core.InputSignal<
|
|
98
|
+
config: _angular_core.InputSignal<SdAutoidInspectorConfiguration | undefined>;
|
|
85
99
|
open: _angular_core.WritableSignal<boolean>;
|
|
86
100
|
segment: _angular_core.WritableSignal<Segment>;
|
|
87
101
|
filter: _angular_core.WritableSignal<string>;
|
|
88
|
-
elements: _angular_core.WritableSignal<
|
|
89
|
-
audit: _angular_core.WritableSignal<
|
|
102
|
+
elements: _angular_core.WritableSignal<AutoidElement[]>;
|
|
103
|
+
audit: _angular_core.WritableSignal<AutoidAuditResult | null>;
|
|
90
104
|
copyOk: _angular_core.WritableSignal<boolean>;
|
|
105
|
+
e2eExporting: _angular_core.WritableSignal<AutoidE2eTarget | null>;
|
|
106
|
+
e2eExportOk: _angular_core.WritableSignal<string | null>;
|
|
107
|
+
e2eExportError: _angular_core.WritableSignal<string | null>;
|
|
108
|
+
robotContextOpen: _angular_core.WritableSignal<boolean>;
|
|
109
|
+
robotExportContext: _angular_core.WritableSignal<AutoidRobotExportContext>;
|
|
91
110
|
highlightOn: _angular_core.WritableSignal<boolean>;
|
|
92
111
|
dismissed: _angular_core.WritableSignal<boolean>;
|
|
93
|
-
|
|
112
|
+
readonly e2eGeneratorHost: _angular_core.Signal<string | undefined>;
|
|
113
|
+
readonly hasRobotExportContext: _angular_core.Signal<boolean>;
|
|
114
|
+
filteredElements: _angular_core.Signal<AutoidElement[]>;
|
|
94
115
|
/**
|
|
95
116
|
* Elements not inside any sd-table — rendered in the main "Top-level" section.
|
|
96
117
|
*/
|
|
97
|
-
readonly topLevelElements: _angular_core.Signal<
|
|
118
|
+
readonly topLevelElements: _angular_core.Signal<AutoidElement[]>;
|
|
98
119
|
/**
|
|
99
120
|
* Map of `sd-table` autoid → its inner autoid elements. Tables themselves
|
|
100
121
|
* stay in `topLevelElements` (the table's own autoid is top-level).
|
|
101
122
|
*/
|
|
102
123
|
readonly tableGroups: _angular_core.Signal<{
|
|
103
124
|
scope: string;
|
|
104
|
-
items:
|
|
125
|
+
items: AutoidElement[];
|
|
105
126
|
}[]>;
|
|
106
127
|
readonly dataOpened: _angular_core.Signal<"true" | "false">;
|
|
107
128
|
readonly dataHighlightOn: _angular_core.Signal<"true" | "false">;
|
|
@@ -123,126 +144,15 @@ declare class SdAutoidInspector implements OnDestroy {
|
|
|
123
144
|
exportJson: () => void;
|
|
124
145
|
exportMdPom: () => void;
|
|
125
146
|
exportMdTable: () => void;
|
|
147
|
+
openRobotExportContext: () => void;
|
|
148
|
+
closeRobotExportContext: () => void;
|
|
149
|
+
updateRobotExportContext: (field: RobotContextField, value: string) => void;
|
|
150
|
+
confirmRobotExport: () => Promise<void>;
|
|
151
|
+
exportE2eTest: (target: AutoidE2eTarget, context?: AutoidRobotExportContext) => Promise<boolean>;
|
|
126
152
|
onEscape(): void;
|
|
127
153
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SdAutoidInspector, never>;
|
|
128
154
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SdAutoidInspector, "sd-autoid-inspector", never, { "enabled": { "alias": "enabled"; "required": false; "isSignal": true; }; "config": { "alias": "config"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
129
155
|
}
|
|
130
156
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
/** `location.pathname + location.search` — kept for back-compat with MD exports. */
|
|
134
|
-
pageUrl: string;
|
|
135
|
-
pageTitle: string;
|
|
136
|
-
timestamp: string;
|
|
137
|
-
/** Full `location.href`. */
|
|
138
|
-
url: string;
|
|
139
|
-
/** `location.pathname` only. */
|
|
140
|
-
pathname: string;
|
|
141
|
-
/** Raw `location.search` (incl. leading `?`), or `''`. */
|
|
142
|
-
search: string;
|
|
143
|
-
/** Parsed query string → key/value map (last value wins on repeats). */
|
|
144
|
-
queryParams: Record<string, string>;
|
|
145
|
-
/**
|
|
146
|
-
* Angular route path params (`:id`, …) merged across the activated-route tree.
|
|
147
|
-
* Only present when an Angular `Router` is available at scan time; omitted otherwise.
|
|
148
|
-
*/
|
|
149
|
-
params?: Record<string, string>;
|
|
150
|
-
}
|
|
151
|
-
/** JSON export envelope: page meta + the scanned elements. */
|
|
152
|
-
interface SdAutoidExportJson {
|
|
153
|
-
meta: SdAutoidExportMeta;
|
|
154
|
-
elements: SdAutoidElement[];
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
declare class SdAutoidScannerService {
|
|
158
|
-
/**
|
|
159
|
-
* Quét element có [data-autoid] trong root, gắn name/tag/value/xpath.
|
|
160
|
-
* Khi truyền `requireSelectors` (vd các sd-* form/button), những node khớp selector
|
|
161
|
-
* mà KHÔNG có data-autoid (self lẫn con) sẽ được thêm vào dưới dạng phần tử fallback
|
|
162
|
-
* (`autoid: ''`, xpath theo vị trí thẻ + cờ `missingAutoid` + warning) — để JSON xuất ra
|
|
163
|
-
* vẫn dùng được và dev biết chỗ cần bổ sung autoid.
|
|
164
|
-
*/
|
|
165
|
-
scan(root?: HTMLElement, requireSelectors?: ReadonlyArray<string>): SdAutoidElement[];
|
|
166
|
-
/**
|
|
167
|
-
* Tìm các node khớp `requireSelectors` nhưng thiếu data-autoid (self + mọi descendant),
|
|
168
|
-
* thêm vào `result` dưới dạng phần tử fallback. Trả về `stt` kế tiếp.
|
|
169
|
-
*/
|
|
170
|
-
private appendMissing;
|
|
171
|
-
/** XPath theo vị trí thẻ trong document, vd `(//sd-button)[2]`, khi không có autoid. */
|
|
172
|
-
private fallbackXpath;
|
|
173
|
-
/** Group element theo autoid để consumer lấy duplicate map. */
|
|
174
|
-
groupByAutoid(elements: SdAutoidElement[]): Record<string, SdAutoidElement[]>;
|
|
175
|
-
/**
|
|
176
|
-
* Read the optional `data-*` E2E state attributes off the same node.
|
|
177
|
-
* Only includes fields actually present on the DOM (skipped fields stay undefined).
|
|
178
|
-
*/
|
|
179
|
-
private readState;
|
|
180
|
-
/**
|
|
181
|
-
* If the node is inside an `<sd-table>` element, return that table's `data-autoid`.
|
|
182
|
-
* Walks up via closest('sd-table') — skips the node itself if it IS the table.
|
|
183
|
-
*/
|
|
184
|
-
private resolveTableScope;
|
|
185
|
-
private resolveName;
|
|
186
|
-
private resolveSdTag;
|
|
187
|
-
private resolveText;
|
|
188
|
-
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SdAutoidScannerService, never>;
|
|
189
|
-
static ɵprov: _angular_core.ɵɵInjectableDeclaration<SdAutoidScannerService>;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
declare class SdAutoidAuditService {
|
|
193
|
-
/**
|
|
194
|
-
* Audit kết quả scan + DOM:
|
|
195
|
-
* - duplicates: map autoid → count khi count > 1
|
|
196
|
-
* - missing: element khớp selector yêu cầu nhưng KHÔNG có [data-autoid]
|
|
197
|
-
*/
|
|
198
|
-
audit(elements: SdAutoidElement[], options?: {
|
|
199
|
-
requireSelectors?: ReadonlyArray<string>;
|
|
200
|
-
root?: HTMLElement;
|
|
201
|
-
}): SdAutoidAuditResult;
|
|
202
|
-
private findMissing;
|
|
203
|
-
private buildSelectorPath;
|
|
204
|
-
private shortHtml;
|
|
205
|
-
private resolveNameHint;
|
|
206
|
-
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SdAutoidAuditService, never>;
|
|
207
|
-
static ɵprov: _angular_core.ɵɵInjectableDeclaration<SdAutoidAuditService>;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
declare class SdAutoidHighlightService {
|
|
211
|
-
#private;
|
|
212
|
-
/** Apply outline cho mọi [data-autoid]: xanh nếu unique, đỏ nếu duplicate. Idempotent. */
|
|
213
|
-
apply(root?: HTMLElement): void;
|
|
214
|
-
/** Highlight tập element không có autoid (missing) bằng outline cam dashed. */
|
|
215
|
-
applyMissing(nodes: HTMLElement[]): void;
|
|
216
|
-
/** Restore inline style như trước khi apply. */
|
|
217
|
-
clear(root?: HTMLElement): void;
|
|
218
|
-
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SdAutoidHighlightService, never>;
|
|
219
|
-
static ɵprov: _angular_core.ɵɵInjectableDeclaration<SdAutoidHighlightService>;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
declare class SdAutoidExportService {
|
|
223
|
-
/**
|
|
224
|
-
* Có `meta` → bọc `{ meta, elements }` (kèm url/queryParams) để consumer biết trang/route.
|
|
225
|
-
* Không `meta` → giữ mảng thuần (back-compat cho caller cũ chỉ cần elements).
|
|
226
|
-
*/
|
|
227
|
-
toJson(elements: SdAutoidElement[], meta?: SdAutoidExportMeta): string;
|
|
228
|
-
/** Parse a `location.search` string into a flat key/value map (last value wins). */
|
|
229
|
-
parseQueryParams(search: string): Record<string, string>;
|
|
230
|
-
toCsv(elements: SdAutoidElement[]): string;
|
|
231
|
-
/** POM TypeScript skeleton — copy ngay vào file Playwright/Cypress. */
|
|
232
|
-
toMarkdownPom(elements: SdAutoidElement[], meta: SdAutoidExportMeta): string;
|
|
233
|
-
/** Markdown reference table, group FIRST by tableScope (top-level first, then per table), then by tag. */
|
|
234
|
-
toMarkdownTable(elements: SdAutoidElement[], meta: SdAutoidExportMeta): string;
|
|
235
|
-
download(content: string, filename: string, mime: string): void;
|
|
236
|
-
copyToClipboard(content: string): Promise<void>;
|
|
237
|
-
/** Build a compact state summary string from defined state keys, e.g. "disabled=false; empty=true". */
|
|
238
|
-
private buildStateSummary;
|
|
239
|
-
private q;
|
|
240
|
-
private mdCell;
|
|
241
|
-
private toCamel;
|
|
242
|
-
private toPascal;
|
|
243
|
-
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SdAutoidExportService, never>;
|
|
244
|
-
static ɵprov: _angular_core.ɵɵInjectableDeclaration<SdAutoidExportService>;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export { SD_AUTOID_DEFAULT_REQUIRE_SELECTORS, SdAutoidAuditService, SdAutoidExportService, SdAutoidHighlightService, SdAutoidInspector, SdAutoidScannerService };
|
|
248
|
-
export type { SdAutoidAuditResult, SdAutoidElement, SdAutoidElementState, SdAutoidExportFormat, SdAutoidExportJson, SdAutoidExportMeta, SdAutoidInspectorConfig, SdAutoidMissing };
|
|
157
|
+
export { SD_AUTOID_INSPECTOR_CONFIGURATION, SdAutoidInspector };
|
|
158
|
+
export type { SdAutoidInspectorConfiguration };
|
|
@@ -154,10 +154,13 @@ declare class SdUploadFile<TArgs = any> {
|
|
|
154
154
|
preview: () => Promise<void>;
|
|
155
155
|
/**
|
|
156
156
|
* Tải xuống một tệp tin dựa trên đối tượng PreviewFile.
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
157
|
+
*
|
|
158
|
+
* Ưu tiên:
|
|
159
|
+
* 1. Nếu consumer CÓ implement hàm download (input `download` hoặc configuration) VÀ file có
|
|
160
|
+
* `idOrKey` (file đã lưu trên server) → LUÔN gọi hàm download. KHÔNG mở `src` (URL lấy từ detail).
|
|
161
|
+
* Dùng cho trường hợp tải file qua API có xác thực thay vì truy cập trực tiếp URL.
|
|
162
|
+
* 2. Ngược lại (không có hàm download, hoặc file local chưa upload nên chưa có `idOrKey`) →
|
|
163
|
+
* tải trực tiếp từ `file` (blob) hoặc `src` (URL).
|
|
161
164
|
*
|
|
162
165
|
* @param previewFile Đối tượng PreviewFile chứa thông tin tệp tin.
|
|
163
166
|
*/
|
|
@@ -10,6 +10,7 @@ export * from '@sdcorejs/angular/components/quick-action';
|
|
|
10
10
|
export * from '@sdcorejs/angular/components/query-bar';
|
|
11
11
|
export * from '@sdcorejs/angular/components/upload-file';
|
|
12
12
|
export * from '@sdcorejs/angular/components/section';
|
|
13
|
+
export * from '@sdcorejs/angular/components/inform';
|
|
13
14
|
export * from '@sdcorejs/angular/components/anchor';
|
|
14
15
|
export * from '@sdcorejs/angular/components/query-builder';
|
|
15
16
|
export * from '@sdcorejs/angular/components/org-chart';
|
|
@@ -25,7 +25,6 @@ declare class SdInputNumber implements OnDestroy, OnInit, AfterViewInit {
|
|
|
25
25
|
sdSuffixDef: _angular_core.Signal<SdSuffixDefDirective | undefined>;
|
|
26
26
|
private ref;
|
|
27
27
|
private coreConfiguration;
|
|
28
|
-
private formatNumberPipe;
|
|
29
28
|
private formConfig;
|
|
30
29
|
autoIdInput: _angular_core.InputSignal<string | null | undefined>;
|
|
31
30
|
autoId: _angular_core.Signal<string | undefined>;
|
|
@@ -8,6 +8,31 @@ interface SdKeycloakTenantConfig {
|
|
|
8
8
|
realm: string;
|
|
9
9
|
clientId: string;
|
|
10
10
|
secureRoutes?: string[];
|
|
11
|
+
/**
|
|
12
|
+
* Basename (KHÔNG kèm đuôi `.html`) của file Keycloak **silent SSO redirect** mà consumer
|
|
13
|
+
* BẮT BUỘC đặt trong thư mục `public/`. Mặc định `'silent-renew'`.
|
|
14
|
+
*
|
|
15
|
+
* Service sẽ truyền `${origin}/<silentRenewUrl>.html` vào `silentCheckSsoRedirectUri`. Với giá trị
|
|
16
|
+
* mặc định, Keycloak gọi `${origin}/silent-renew.html` (giống hành vi cũ — không phá vỡ project hiện có).
|
|
17
|
+
*
|
|
18
|
+
* Consumer cần tạo file tĩnh này (nội dung mẫu ở `modules/keycloak/htmls/silent-renew.html`); nếu THIẾU,
|
|
19
|
+
* silent `check-sso` thất bại và `init()` trả về `false` không mong muốn (user bị coi là chưa đăng nhập).
|
|
20
|
+
* Cho phép truyền kèm `/` đầu hoặc đuôi `.html` (tự chuẩn hoá), hoặc đặt sâu hơn:
|
|
21
|
+
* `'assets/silent-renew'` → `${origin}/assets/silent-renew.html`.
|
|
22
|
+
*/
|
|
23
|
+
silentRenewUrl?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Basename (KHÔNG kèm đuôi `.html`) của **trang lỗi tĩnh** trong `public/`, dùng làm đích redirect khi
|
|
26
|
+
* `keycloak.init()` NÉM LỖI (Keycloak sập / mạng lỗi / cấu hình realm sai). Mặc định `'auth-keycloak-error'`.
|
|
27
|
+
*
|
|
28
|
+
* Khi init thất bại, app điều hướng full-page tới `${origin}/<authErrorUrl>.html`. Vì là file tĩnh nên
|
|
29
|
+
* trang lỗi vẫn hiển thị kể cả khi bundle Angular chưa khởi tạo được. Service cũng tự bỏ qua `init()` nếu
|
|
30
|
+
* đang đứng sẵn ở trang lỗi này (chống vòng lặp redirect).
|
|
31
|
+
*
|
|
32
|
+
* Consumer nên tạo file (nội dung mẫu ở `modules/keycloak/htmls/auth-keycloak-error.html`) và viết lại
|
|
33
|
+
* branding/nội dung. Nếu THIẾU, redirect rơi vào trang 404 của host. Chuẩn hoá giá trị giống `silentRenewUrl`.
|
|
34
|
+
*/
|
|
35
|
+
authErrorUrl?: string;
|
|
11
36
|
}
|
|
12
37
|
interface ISdKeycloakConfiguration {
|
|
13
38
|
loadTenantConfig: () => Promise<SdKeycloakTenantConfig>;
|
|
@@ -18,6 +43,11 @@ declare class SdKeycloakService {
|
|
|
18
43
|
keycloak: Keycloak;
|
|
19
44
|
config: SdKeycloakTenantConfig;
|
|
20
45
|
init(config: SdKeycloakTenantConfig): Promise<boolean>;
|
|
46
|
+
/**
|
|
47
|
+
* Chuẩn hoá basename file tĩnh trong public/ thành URL tuyệt đối.
|
|
48
|
+
* `'silent-renew'` | `'/silent-renew'` | `'silent-renew.html'` → `${origin}/silent-renew.html`.
|
|
49
|
+
*/
|
|
50
|
+
private toPublicHtmlUrl;
|
|
21
51
|
login(): Promise<void>;
|
|
22
52
|
logout(): Promise<void>;
|
|
23
53
|
getToken(): string | undefined;
|