@trismegisto/spine 2.1.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -17
- package/fesm2022/trismegisto-spine-audit.mjs +264 -10
- package/fesm2022/trismegisto-spine-audit.mjs.map +1 -1
- package/fesm2022/trismegisto-spine-auto-service-apps.technical-BAKdkVdz.mjs +84 -0
- package/fesm2022/trismegisto-spine-auto-service-apps.technical-BAKdkVdz.mjs.map +1 -0
- package/fesm2022/trismegisto-spine-auto-service-apps.usage-CAXMP5r8.mjs +87 -0
- package/fesm2022/trismegisto-spine-auto-service-apps.usage-CAXMP5r8.mjs.map +1 -0
- package/fesm2022/{trismegisto-spine-auto-service-audit.technical-C901aIIQ.mjs → trismegisto-spine-auto-service-audit.technical-B75d4mh2.mjs} +6 -5
- package/fesm2022/trismegisto-spine-auto-service-audit.technical-B75d4mh2.mjs.map +1 -0
- package/fesm2022/{trismegisto-spine-auto-service-audit.usage-GIJ2RwuH.mjs → trismegisto-spine-auto-service-audit.usage-IdXMruv5.mjs} +9 -8
- package/fesm2022/trismegisto-spine-auto-service-audit.usage-IdXMruv5.mjs.map +1 -0
- package/fesm2022/{trismegisto-spine-auto-service-auto-service-audit.component-CCiz-q8o.mjs → trismegisto-spine-auto-service-auto-service-audit.component-BIL8tH1I.mjs} +4 -4
- package/fesm2022/{trismegisto-spine-auto-service-auto-service-audit.component-CCiz-q8o.mjs.map → trismegisto-spine-auto-service-auto-service-audit.component-BIL8tH1I.mjs.map} +1 -1
- package/fesm2022/{trismegisto-spine-auto-service-auto-service-client-picker.component-Z0wCANF6.mjs → trismegisto-spine-auto-service-auto-service-client-picker.component-CBt_0Uli.mjs} +3 -3
- package/fesm2022/{trismegisto-spine-auto-service-auto-service-client-picker.component-Z0wCANF6.mjs.map → trismegisto-spine-auto-service-auto-service-client-picker.component-CBt_0Uli.mjs.map} +1 -1
- package/fesm2022/{trismegisto-spine-auto-service-auto-service-client.component-C7SPavrX.mjs → trismegisto-spine-auto-service-auto-service-client.component-DAxhZC-a.mjs} +123 -27
- package/fesm2022/trismegisto-spine-auto-service-auto-service-client.component-DAxhZC-a.mjs.map +1 -0
- package/fesm2022/trismegisto-spine-auto-service-auto-service-delete-CoJYTtBU.mjs +87 -0
- package/fesm2022/trismegisto-spine-auto-service-auto-service-delete-CoJYTtBU.mjs.map +1 -0
- package/fesm2022/trismegisto-spine-auto-service-auto-service-groups.component-UVMGF7-B.mjs +544 -0
- package/fesm2022/trismegisto-spine-auto-service-auto-service-groups.component-UVMGF7-B.mjs.map +1 -0
- package/fesm2022/trismegisto-spine-auto-service-auto-service-login-attempts.component-Boi-kG0G.mjs +37 -0
- package/fesm2022/trismegisto-spine-auto-service-auto-service-login-attempts.component-Boi-kG0G.mjs.map +1 -0
- package/fesm2022/trismegisto-spine-auto-service-auto-service.component-DmLafUkX.mjs +943 -0
- package/fesm2022/trismegisto-spine-auto-service-auto-service.component-DmLafUkX.mjs.map +1 -0
- package/fesm2022/{trismegisto-spine-auto-service-client.technical-BHWhpyWv.mjs → trismegisto-spine-auto-service-client.technical-DP5G8i3o.mjs} +24 -3
- package/fesm2022/trismegisto-spine-auto-service-client.technical-DP5G8i3o.mjs.map +1 -0
- package/fesm2022/{trismegisto-spine-auto-service-client.usage-CM2WB2e2.mjs → trismegisto-spine-auto-service-client.usage-CcJANb20.mjs} +15 -6
- package/fesm2022/trismegisto-spine-auto-service-client.usage-CcJANb20.mjs.map +1 -0
- package/fesm2022/trismegisto-spine-auto-service-groups.technical-BlktCM70.mjs +65 -0
- package/fesm2022/trismegisto-spine-auto-service-groups.technical-BlktCM70.mjs.map +1 -0
- package/fesm2022/trismegisto-spine-auto-service-groups.usage-Cz5sCKgn.mjs +81 -0
- package/fesm2022/trismegisto-spine-auto-service-groups.usage-Cz5sCKgn.mjs.map +1 -0
- package/fesm2022/{trismegisto-spine-auto-service-home.component-vl3BPFnk.mjs → trismegisto-spine-auto-service-home.component-5_aji1hf.mjs} +5 -4
- package/fesm2022/trismegisto-spine-auto-service-home.component-5_aji1hf.mjs.map +1 -0
- package/fesm2022/trismegisto-spine-auto-service-login-attempts.technical-CasKIIws.mjs +37 -0
- package/fesm2022/trismegisto-spine-auto-service-login-attempts.technical-CasKIIws.mjs.map +1 -0
- package/fesm2022/trismegisto-spine-auto-service-login-attempts.usage-BMgLNu7W.mjs +60 -0
- package/fesm2022/trismegisto-spine-auto-service-login-attempts.usage-BMgLNu7W.mjs.map +1 -0
- package/fesm2022/trismegisto-spine-auto-service.mjs +49 -29
- package/fesm2022/trismegisto-spine-auto-service.mjs.map +1 -1
- package/fesm2022/trismegisto-spine-layout.mjs +1698 -156
- package/fesm2022/trismegisto-spine-layout.mjs.map +1 -1
- package/fesm2022/trismegisto-spine-shell.mjs +17 -1
- package/fesm2022/trismegisto-spine-shell.mjs.map +1 -1
- package/fesm2022/trismegisto-spine.mjs +1157 -85
- package/fesm2022/trismegisto-spine.mjs.map +1 -1
- package/package.json +6 -6
- package/types/trismegisto-spine-audit.d.ts +180 -5
- package/types/trismegisto-spine-audit.d.ts.map +1 -1
- package/types/trismegisto-spine-auto-service.d.ts +100 -7
- package/types/trismegisto-spine-auto-service.d.ts.map +1 -1
- package/types/trismegisto-spine-layout.d.ts +833 -13
- package/types/trismegisto-spine-layout.d.ts.map +1 -1
- package/types/trismegisto-spine-shell.d.ts +8 -0
- package/types/trismegisto-spine-shell.d.ts.map +1 -1
- package/types/trismegisto-spine.d.ts +491 -32
- package/types/trismegisto-spine.d.ts.map +1 -1
- package/fesm2022/trismegisto-spine-auto-service-apps.technical-ClMKB9cK.mjs +0 -48
- package/fesm2022/trismegisto-spine-auto-service-apps.technical-ClMKB9cK.mjs.map +0 -1
- package/fesm2022/trismegisto-spine-auto-service-apps.usage-Cf5SG-oQ.mjs +0 -55
- package/fesm2022/trismegisto-spine-auto-service-apps.usage-Cf5SG-oQ.mjs.map +0 -1
- package/fesm2022/trismegisto-spine-auto-service-audit.technical-C901aIIQ.mjs.map +0 -1
- package/fesm2022/trismegisto-spine-auto-service-audit.usage-GIJ2RwuH.mjs.map +0 -1
- package/fesm2022/trismegisto-spine-auto-service-auto-service-client.component-C7SPavrX.mjs.map +0 -1
- package/fesm2022/trismegisto-spine-auto-service-auto-service-delete-6Bk46MOf.mjs +0 -39
- package/fesm2022/trismegisto-spine-auto-service-auto-service-delete-6Bk46MOf.mjs.map +0 -1
- package/fesm2022/trismegisto-spine-auto-service-auto-service-groups.component-CqIVe2LU.mjs +0 -357
- package/fesm2022/trismegisto-spine-auto-service-auto-service-groups.component-CqIVe2LU.mjs.map +0 -1
- package/fesm2022/trismegisto-spine-auto-service-auto-service.component-BLN49F4a.mjs +0 -502
- package/fesm2022/trismegisto-spine-auto-service-auto-service.component-BLN49F4a.mjs.map +0 -1
- package/fesm2022/trismegisto-spine-auto-service-client.technical-BHWhpyWv.mjs.map +0 -1
- package/fesm2022/trismegisto-spine-auto-service-client.usage-CM2WB2e2.mjs.map +0 -1
- package/fesm2022/trismegisto-spine-auto-service-groups.technical-jQlwaQPn.mjs +0 -39
- package/fesm2022/trismegisto-spine-auto-service-groups.technical-jQlwaQPn.mjs.map +0 -1
- package/fesm2022/trismegisto-spine-auto-service-groups.usage-Bh7ig2zZ.mjs +0 -54
- package/fesm2022/trismegisto-spine-auto-service-groups.usage-Bh7ig2zZ.mjs.map +0 -1
- package/fesm2022/trismegisto-spine-auto-service-home.component-vl3BPFnk.mjs.map +0 -1
|
@@ -9,7 +9,7 @@ import { pluralMessage, formatMessage } from '@trismegisto/primitives';
|
|
|
9
9
|
import { spineRoutesOf, SpineQuickAccessService, SPINE_MESSAGES } from '@trismegisto/spine';
|
|
10
10
|
import { SpineDocsService, SpineSettingsService } from '@trismegisto/spine/layout';
|
|
11
11
|
import { map } from 'rxjs';
|
|
12
|
-
import { AUTO_SERVICE_GROUPS_TEMPLATE_ROUTER_ID, AUTO_SERVICE_APPS_TEMPLATE_ROUTER_ID, AUTO_SERVICE_CLIENT_TEMPLATE_ROUTER_ID, AUTO_SERVICE_AUDIT_TEMPLATE_ROUTER_ID, AUTO_SERVICE_HOME_TEMPLATE_ROUTER_ID } from './trismegisto-spine-auto-service.mjs';
|
|
12
|
+
import { AUTO_SERVICE_GROUPS_TEMPLATE_ROUTER_ID, AUTO_SERVICE_APPS_TEMPLATE_ROUTER_ID, AUTO_SERVICE_CLIENT_TEMPLATE_ROUTER_ID, AUTO_SERVICE_AUDIT_TEMPLATE_ROUTER_ID, AUTO_SERVICE_LOGIN_ATTEMPTS_TEMPLATE_ROUTER_ID, AUTO_SERVICE_HOME_TEMPLATE_ROUTER_ID } from './trismegisto-spine-auto-service.mjs';
|
|
13
13
|
|
|
14
14
|
// Above this many tiles a grid stops being scannable at a glance.
|
|
15
15
|
const MAX_TILES = 12;
|
|
@@ -18,6 +18,7 @@ const ADMIN_TEMPLATES = new Set([
|
|
|
18
18
|
AUTO_SERVICE_APPS_TEMPLATE_ROUTER_ID,
|
|
19
19
|
AUTO_SERVICE_CLIENT_TEMPLATE_ROUTER_ID,
|
|
20
20
|
AUTO_SERVICE_AUDIT_TEMPLATE_ROUTER_ID,
|
|
21
|
+
AUTO_SERVICE_LOGIN_ATTEMPTS_TEMPLATE_ROUTER_ID,
|
|
21
22
|
].map((id) => id.toLowerCase()));
|
|
22
23
|
const HOME_TEMPLATE = AUTO_SERVICE_HOME_TEMPLATE_ROUTER_ID.toLowerCase();
|
|
23
24
|
function childrenOf(node) {
|
|
@@ -227,12 +228,12 @@ class HomeComponent {
|
|
|
227
228
|
this.docs.openIndex();
|
|
228
229
|
}
|
|
229
230
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: HomeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
230
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: HomeComponent, isStandalone: true, selector: "home", ngImport: i0, template: "@if (noAccess()) {\n <hm-state-page kind=\"no-rights\" [heading]=\"t.noAccessTitle\" [description]=\"t.noAccessText\" action=\"\" />\n} @else {\n <div class=\"hub\" [class.hub--columns]=\"sideBySide()\">\n @if (workTiles().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-work'\">\n <h2 class=\"block__title\" [id]=\"uid + '-work'\">{{ descended() ? t.yourScreens : t.modules }}</h2>\n <ng-container *ngTemplateOutlet=\"tileGrid; context: { $implicit: workTiles(), admin: false }\" />\n </section>\n }\n\n @if (adminTiles().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-admin'\">\n <h2 class=\"block__title\" [id]=\"uid + '-admin'\">{{ t.privileged }}</h2>\n <ng-container *ngTemplateOutlet=\"tileGrid; context: { $implicit: adminTiles(), admin: true }\" />\n </section>\n }\n\n @if (recentRows().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-recent'\">\n <h2 class=\"block__title\" [id]=\"uid + '-recent'\">{{ t.recent }}</h2>\n <hm-data-grid\n [caption]=\"t.recentCaption\"\n [columns]=\"recentColumns\"\n [rows]=\"recentRows()\"\n [rowId]=\"rowId\"\n [filterable]=\"false\"\n [paginated]=\"false\"\n >\n <ng-template hmCell=\"displayName\" let-value>\n @let item = row(value);\n <button type=\"button\" class=\"link\" (click)=\"go(item.redirect)\">\n <hm-icon [shape]=\"item.icon || 'grid-layout'\" size=\"sm\" />\n <span>{{ item.displayName }}</span>\n </button>\n </ng-template>\n <ng-template hmCell=\"favorite\" let-value>\n @let item = row(value);\n <hm-icon-button\n emphasis=\"subtle\"\n icon=\"heart\"\n [solid]=\"isFavorite(item.id)\"\n [class.heart--on]=\"isFavorite(item.id)\"\n [label]=\"favoriteLabel(item)\"\n (clicked)=\"toggleFavorite(item)\"\n />\n </ng-template>\n </hm-data-grid>\n </section>\n }\n\n @if (favorites().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-fav'\">\n <h2 class=\"block__title\" [id]=\"uid + '-fav'\">{{ t.favorites }}</h2>\n <ul class=\"chips\">\n @for (fav of favorites(); track fav.id) {\n <li>\n <button type=\"button\" class=\"chip\" [title]=\"whereOf(fav)\" (click)=\"go(fav.redirect)\">\n <hm-icon [shape]=\"fav.icon || 'grid-layout'\" size=\"sm\" />\n <span>{{ fav.displayName }}</span>\n </button>\n </li>\n }\n </ul>\n </section>\n }\n\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-nav'\">\n <h2 class=\"block__title\" [id]=\"uid + '-nav'\">{{ t.navigate }}</h2>\n <ul class=\"chips\">\n @for (area of areas(); track area.routed_id) {\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"go(area.route_redirect)\">\n <hm-icon [shape]=\"area.route_icon || 'home'\" size=\"sm\" />\n <span>{{ area.route_display_name }}</span>\n </button>\n </li>\n }\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"openSettings()\">\n <hm-icon shape=\"cog\" size=\"sm\" />\n <span>{{ t.settings }}</span>\n </button>\n </li>\n </ul>\n </section>\n\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-tools'\">\n <h2 class=\"block__title\" [id]=\"uid + '-tools'\">{{ t.tools }}</h2>\n <ul class=\"chips\">\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"openDocs()\">\n <hm-icon shape=\"doc-list\" size=\"sm\" />\n <span>{{ t.systemDocs }}</span>\n </button>\n </li>\n </ul>\n </section>\n </div>\n}\n\n<ng-template #tileGrid let-tiles let-admin=\"admin\">\n <ul class=\"tiles\" [class.tiles--admin]=\"admin\">\n @for (tile of tiles; track tile.id; let i = $index) {\n <li class=\"tiles__cell\" [style.--tile-order]=\"i\">\n <button type=\"button\" class=\"tile\" (click)=\"go(tile.redirect)\">\n <hm-icon class=\"tile__icon\" [shape]=\"tile.icon ?? 'grid-layout'\" size=\"lg\" />\n <span class=\"tile__text\">\n <span class=\"tile__label\">{{ tile.label }}</span>\n @if (caption(tile); as meta) {\n <span class=\"tile__meta\">{{ meta }}</span>\n }\n </span>\n </button>\n </li>\n }\n </ul>\n</ng-template>\n", styles: [":host{display:flex;flex:1 1 auto;flex-direction:column;min-block-size:100%;min-inline-size:0}hm-state-page{flex:1 1 auto}.hub{display:flex;flex-direction:column;gap:var(--hm-space-xl);padding:var(--hm-space-lg);min-inline-size:0}.hub--columns{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,20rem),1fr));align-items:start}.block{display:flex;flex-direction:column;gap:var(--hm-space-sm);min-inline-size:0}.block__title{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}ul{margin:0;padding:0;list-style:none}.link,.chip,.tile{display:inline-flex;align-items:center;gap:var(--hm-space-xs);border:1px solid transparent;border-radius:var(--hm-radius-control);background:transparent;color:var(--hm-text);font:inherit;text-align:start;cursor:pointer}.link:hover,.chip:hover,.tile:hover{background:var(--hm-hover)}.link:focus-visible,.chip:focus-visible,.tile:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:2px}@media(forced-colors:active){.link,.chip,.tile{border-color:ButtonText}.link:focus-visible,.chip:focus-visible,.tile:focus-visible{outline-color:Highlight}}.tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,13rem),1fr));gap:var(--hm-space-sm)}.tiles__cell{display:flex}.tile{flex:1 1 auto;gap:var(--hm-space-md);padding:var(--hm-space-md);border-color:var(--hm-divider);background:var(--hm-surface-raised)}.tile__icon{flex:none;color:var(--hm-accent)}.tiles--admin .tile__icon{color:var(--hm-warning)}.tile__text{display:flex;flex-direction:column;gap:var(--hm-space-nano);min-inline-size:0}.tile__label{font-size:var(--hm-font-size-md);font-weight:var(--hm-font-weight-medium);overflow-wrap:anywhere}.tile__meta{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}@media(prefers-reduced-motion:no-preference){.tiles__cell{animation:tile-in var(--hm-duration-standard) var(--hm-ease-decelerate) both;animation-delay:calc(min(var(--tile-order, 0),10)*35ms)}}@keyframes tile-in{0%{opacity:0;transform:translateY(var(--hm-space-xs))}}.chips{display:flex;flex-wrap:wrap;gap:var(--hm-space-xs)}.chip{padding:var(--hm-space-xs) var(--hm-space-sm);border-color:var(--hm-divider);background:var(--hm-surface)}.link{padding:var(--hm-space-nano) var(--hm-space-xs);margin-inline-start:calc(-1 * var(--hm-space-xs));color:var(--hm-accent)}.heart--on{color:var(--hm-danger)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DataGridComponent, selector: "hm-data-grid", inputs: ["columns", "rows", "rowId", "caption", "rowActions", "loading", "error", "emptyMessage", "noResultsMessage", "filterable", "paginated", "pageState", "pagination", "totalItems", "sort", "selectable", "selectionMode", "selection", "rowSelectable", "rowSelectableReason", "rowDetailDisabled", "rowDetailHidden", "virtual", "rowHeight", "pinnedColumns", "columnState", "pageSizes", "fillHeight", "maxHeight", "bordered", "rowState", "rowClickable", "remoteSearch", "remoteSort", "itemsLabel", "pageSizeLabel", "exportable", "exportFileName", "groupBy", "aggregates", "aggregateFormatter", "persistKey", "persistStorage", "advancedFilter", "filterQuery", "columnManager", "columnOrder", "searchTerm"], outputs: ["pageStateChange", "sortChange", "selectionChange", "pinnedColumnsChange", "columnStateChange", "actionTriggered", "detailToggled", "rowClicked", "clearFiltersRequested", "filteredChange", "searchTermChanged", "groupByChange", "filterQueryChange", "columnOrderChange", "searchTermChange", "detailPaneToggled"] }, { kind: "directive", type: DataGridCellDirective, selector: "ng-template[hmCell]", inputs: ["hmCell"] }, { kind: "component", type: IconButtonComponent, selector: "hm-icon-button", inputs: ["label", "icon", "solid", "iconDirection", "emphasis", "intent", "size", "disabled", "toggle", "pressed", "keyShortcuts"], outputs: ["pressedChange", "clicked"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: StatePageComponent, selector: "hm-state-page", inputs: ["kind", "heading", "description", "action", "actionIcon"], outputs: ["actionTriggered"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
231
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: HomeComponent, isStandalone: true, selector: "home", ngImport: i0, template: "@if (noAccess()) {\n <hm-state-page kind=\"no-rights\" [heading]=\"t.noAccessTitle\" [description]=\"t.noAccessText\" action=\"\" />\n} @else {\n <div class=\"hub\" [class.hub--columns]=\"sideBySide()\">\n @if (workTiles().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-work'\">\n <h2 class=\"block__title\" [id]=\"uid + '-work'\">{{ descended() ? t.yourScreens : t.modules }}</h2>\n <ng-container *ngTemplateOutlet=\"tileGrid; context: { $implicit: workTiles(), admin: false }\" />\n </section>\n }\n\n @if (adminTiles().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-admin'\">\n <h2 class=\"block__title\" [id]=\"uid + '-admin'\">{{ t.privileged }}</h2>\n <ng-container *ngTemplateOutlet=\"tileGrid; context: { $implicit: adminTiles(), admin: true }\" />\n </section>\n }\n\n @if (recentRows().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-recent'\">\n <h2 class=\"block__title\" [id]=\"uid + '-recent'\">{{ t.recent }}</h2>\n <hm-data-grid\n [caption]=\"t.recentCaption\"\n [columns]=\"recentColumns\"\n [rows]=\"recentRows()\"\n [rowId]=\"rowId\"\n [filterable]=\"false\"\n [paginated]=\"false\"\n >\n <ng-template hmCell=\"displayName\" let-value>\n @let item = row(value);\n <button type=\"button\" class=\"link\" (click)=\"go(item.redirect)\">\n <hm-icon [shape]=\"item.icon || 'grid-layout'\" size=\"sm\" />\n <span>{{ item.displayName }}</span>\n </button>\n </ng-template>\n <ng-template hmCell=\"favorite\" let-value>\n @let item = row(value);\n <hm-icon-button\n emphasis=\"subtle\"\n icon=\"heart\"\n [solid]=\"isFavorite(item.id)\"\n [class.heart--on]=\"isFavorite(item.id)\"\n [label]=\"favoriteLabel(item)\"\n (clicked)=\"toggleFavorite(item)\"\n />\n </ng-template>\n </hm-data-grid>\n </section>\n }\n\n @if (favorites().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-fav'\">\n <h2 class=\"block__title\" [id]=\"uid + '-fav'\">{{ t.favorites }}</h2>\n <ul class=\"chips\">\n @for (fav of favorites(); track fav.id) {\n <li>\n <button type=\"button\" class=\"chip\" [title]=\"whereOf(fav)\" (click)=\"go(fav.redirect)\">\n <hm-icon [shape]=\"fav.icon || 'grid-layout'\" size=\"sm\" />\n <span>{{ fav.displayName }}</span>\n </button>\n </li>\n }\n </ul>\n </section>\n }\n\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-nav'\">\n <h2 class=\"block__title\" [id]=\"uid + '-nav'\">{{ t.navigate }}</h2>\n <ul class=\"chips\">\n @for (area of areas(); track area.routed_id) {\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"go(area.route_redirect)\">\n <hm-icon [shape]=\"area.route_icon || 'home'\" size=\"sm\" />\n <span>{{ area.route_display_name }}</span>\n </button>\n </li>\n }\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"openSettings()\">\n <hm-icon shape=\"cog\" size=\"sm\" />\n <span>{{ t.settings }}</span>\n </button>\n </li>\n </ul>\n </section>\n\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-tools'\">\n <h2 class=\"block__title\" [id]=\"uid + '-tools'\">{{ t.tools }}</h2>\n <ul class=\"chips\">\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"openDocs()\">\n <hm-icon shape=\"doc-list\" size=\"sm\" />\n <span>{{ t.systemDocs }}</span>\n </button>\n </li>\n </ul>\n </section>\n </div>\n}\n\n<ng-template #tileGrid let-tiles let-admin=\"admin\">\n <ul class=\"tiles\" [class.tiles--admin]=\"admin\">\n @for (tile of tiles; track tile.id; let i = $index) {\n <li class=\"tiles__cell\" [style.--tile-order]=\"i\">\n <button type=\"button\" class=\"tile\" (click)=\"go(tile.redirect)\">\n <hm-icon class=\"tile__icon\" [shape]=\"tile.icon ?? 'grid-layout'\" size=\"lg\" />\n <span class=\"tile__text\">\n <span class=\"tile__label\">{{ tile.label }}</span>\n @if (caption(tile); as meta) {\n <span class=\"tile__meta\">{{ meta }}</span>\n }\n </span>\n </button>\n </li>\n }\n </ul>\n</ng-template>\n", styles: [":host{display:flex;flex:1 1 auto;flex-direction:column;min-block-size:100%;min-inline-size:0}hm-state-page{flex:1 1 auto}.hub{display:flex;flex-direction:column;gap:var(--hm-space-xl);padding:var(--hm-space-lg);min-inline-size:0}.hub--columns{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,20rem),1fr));align-items:start}.block{display:flex;flex-direction:column;gap:var(--hm-space-sm);min-inline-size:0}.block__title{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}ul{margin:0;padding:0;list-style:none}.link,.chip,.tile{display:inline-flex;align-items:center;gap:var(--hm-space-xs);border:1px solid transparent;border-radius:var(--hm-radius-control);background:transparent;color:var(--hm-text);font:inherit;text-align:start;cursor:pointer}.link:hover,.chip:hover,.tile:hover{background:var(--hm-hover)}.link:focus-visible,.chip:focus-visible,.tile:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:2px}@media(forced-colors:active){.link,.chip,.tile{border-color:ButtonText}.link:focus-visible,.chip:focus-visible,.tile:focus-visible{outline-color:Highlight}}.tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,13rem),1fr));gap:var(--hm-space-sm)}.tiles__cell{display:flex}.tile{flex:1 1 auto;gap:var(--hm-space-md);padding:var(--hm-space-md);border-color:var(--hm-divider);background:var(--hm-surface-raised)}.tile__icon{flex:none;color:var(--hm-accent)}.tiles--admin .tile__icon{color:var(--hm-warning)}.tile__text{display:flex;flex-direction:column;gap:var(--hm-space-nano);min-inline-size:0}.tile__label{font-size:var(--hm-font-size-md);font-weight:var(--hm-font-weight-medium);overflow-wrap:anywhere}.tile__meta{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}@media(prefers-reduced-motion:no-preference){.tiles__cell{animation:tile-in var(--hm-duration-standard) var(--hm-ease-decelerate) both;animation-delay:calc(min(var(--tile-order, 0),10)*35ms)}}:host-context([data-motion=full]) .tiles__cell{animation:tile-in var(--hm-duration-standard) var(--hm-ease-decelerate) both;animation-delay:calc(min(var(--tile-order, 0),10)*35ms)}:host-context([data-motion=reduce]) .tiles__cell{animation:none}@keyframes tile-in{0%{opacity:0;transform:translateY(var(--hm-space-xs))}}.chips{display:flex;flex-wrap:wrap;gap:var(--hm-space-xs)}.chip{padding:var(--hm-space-xs) var(--hm-space-sm);border-color:var(--hm-divider);background:var(--hm-surface)}.link{padding:var(--hm-space-nano) var(--hm-space-xs);margin-inline-start:calc(-1 * var(--hm-space-xs));color:var(--hm-accent)}.heart--on{color:var(--hm-danger)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DataGridComponent, selector: "hm-data-grid", inputs: ["columns", "rows", "rowId", "caption", "rowActions", "loading", "error", "emptyMessage", "noResultsMessage", "filterable", "paginated", "pageState", "pagination", "totalItems", "sort", "selectable", "selectionMode", "selection", "rowSelectable", "rowSelectableReason", "rowDetailDisabled", "rowDetailHidden", "virtual", "rowHeight", "pinnedColumns", "columnState", "pageSizes", "fillHeight", "maxHeight", "bordered", "rowState", "rowClickable", "remoteSearch", "remoteSort", "itemsLabel", "pageSizeLabel", "exportable", "exportFileName", "groupBy", "aggregates", "aggregateFormatter", "persistKey", "persistStorage", "advancedFilter", "filterQuery", "columnManager", "columnOrder", "searchTerm"], outputs: ["pageStateChange", "sortChange", "selectionChange", "pinnedColumnsChange", "columnStateChange", "actionTriggered", "detailToggled", "rowClicked", "clearFiltersRequested", "filteredChange", "searchTermChanged", "groupByChange", "filterQueryChange", "columnOrderChange", "searchTermChange", "detailPaneToggled"] }, { kind: "directive", type: DataGridCellDirective, selector: "ng-template[hmCell]", inputs: ["hmCell"] }, { kind: "component", type: IconButtonComponent, selector: "hm-icon-button", inputs: ["label", "icon", "solid", "iconDirection", "emphasis", "intent", "size", "disabled", "toggle", "pressed", "keyShortcuts"], outputs: ["pressedChange", "clicked"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: StatePageComponent, selector: "hm-state-page", inputs: ["kind", "heading", "description", "action", "actionIcon"], outputs: ["actionTriggered"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
231
232
|
}
|
|
232
233
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: HomeComponent, decorators: [{
|
|
233
234
|
type: Component,
|
|
234
|
-
args: [{ selector: 'home', imports: [NgTemplateOutlet, DataGridComponent, DataGridCellDirective, IconButtonComponent, IconComponent, StatePageComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (noAccess()) {\n <hm-state-page kind=\"no-rights\" [heading]=\"t.noAccessTitle\" [description]=\"t.noAccessText\" action=\"\" />\n} @else {\n <div class=\"hub\" [class.hub--columns]=\"sideBySide()\">\n @if (workTiles().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-work'\">\n <h2 class=\"block__title\" [id]=\"uid + '-work'\">{{ descended() ? t.yourScreens : t.modules }}</h2>\n <ng-container *ngTemplateOutlet=\"tileGrid; context: { $implicit: workTiles(), admin: false }\" />\n </section>\n }\n\n @if (adminTiles().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-admin'\">\n <h2 class=\"block__title\" [id]=\"uid + '-admin'\">{{ t.privileged }}</h2>\n <ng-container *ngTemplateOutlet=\"tileGrid; context: { $implicit: adminTiles(), admin: true }\" />\n </section>\n }\n\n @if (recentRows().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-recent'\">\n <h2 class=\"block__title\" [id]=\"uid + '-recent'\">{{ t.recent }}</h2>\n <hm-data-grid\n [caption]=\"t.recentCaption\"\n [columns]=\"recentColumns\"\n [rows]=\"recentRows()\"\n [rowId]=\"rowId\"\n [filterable]=\"false\"\n [paginated]=\"false\"\n >\n <ng-template hmCell=\"displayName\" let-value>\n @let item = row(value);\n <button type=\"button\" class=\"link\" (click)=\"go(item.redirect)\">\n <hm-icon [shape]=\"item.icon || 'grid-layout'\" size=\"sm\" />\n <span>{{ item.displayName }}</span>\n </button>\n </ng-template>\n <ng-template hmCell=\"favorite\" let-value>\n @let item = row(value);\n <hm-icon-button\n emphasis=\"subtle\"\n icon=\"heart\"\n [solid]=\"isFavorite(item.id)\"\n [class.heart--on]=\"isFavorite(item.id)\"\n [label]=\"favoriteLabel(item)\"\n (clicked)=\"toggleFavorite(item)\"\n />\n </ng-template>\n </hm-data-grid>\n </section>\n }\n\n @if (favorites().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-fav'\">\n <h2 class=\"block__title\" [id]=\"uid + '-fav'\">{{ t.favorites }}</h2>\n <ul class=\"chips\">\n @for (fav of favorites(); track fav.id) {\n <li>\n <button type=\"button\" class=\"chip\" [title]=\"whereOf(fav)\" (click)=\"go(fav.redirect)\">\n <hm-icon [shape]=\"fav.icon || 'grid-layout'\" size=\"sm\" />\n <span>{{ fav.displayName }}</span>\n </button>\n </li>\n }\n </ul>\n </section>\n }\n\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-nav'\">\n <h2 class=\"block__title\" [id]=\"uid + '-nav'\">{{ t.navigate }}</h2>\n <ul class=\"chips\">\n @for (area of areas(); track area.routed_id) {\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"go(area.route_redirect)\">\n <hm-icon [shape]=\"area.route_icon || 'home'\" size=\"sm\" />\n <span>{{ area.route_display_name }}</span>\n </button>\n </li>\n }\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"openSettings()\">\n <hm-icon shape=\"cog\" size=\"sm\" />\n <span>{{ t.settings }}</span>\n </button>\n </li>\n </ul>\n </section>\n\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-tools'\">\n <h2 class=\"block__title\" [id]=\"uid + '-tools'\">{{ t.tools }}</h2>\n <ul class=\"chips\">\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"openDocs()\">\n <hm-icon shape=\"doc-list\" size=\"sm\" />\n <span>{{ t.systemDocs }}</span>\n </button>\n </li>\n </ul>\n </section>\n </div>\n}\n\n<ng-template #tileGrid let-tiles let-admin=\"admin\">\n <ul class=\"tiles\" [class.tiles--admin]=\"admin\">\n @for (tile of tiles; track tile.id; let i = $index) {\n <li class=\"tiles__cell\" [style.--tile-order]=\"i\">\n <button type=\"button\" class=\"tile\" (click)=\"go(tile.redirect)\">\n <hm-icon class=\"tile__icon\" [shape]=\"tile.icon ?? 'grid-layout'\" size=\"lg\" />\n <span class=\"tile__text\">\n <span class=\"tile__label\">{{ tile.label }}</span>\n @if (caption(tile); as meta) {\n <span class=\"tile__meta\">{{ meta }}</span>\n }\n </span>\n </button>\n </li>\n }\n </ul>\n</ng-template>\n", styles: [":host{display:flex;flex:1 1 auto;flex-direction:column;min-block-size:100%;min-inline-size:0}hm-state-page{flex:1 1 auto}.hub{display:flex;flex-direction:column;gap:var(--hm-space-xl);padding:var(--hm-space-lg);min-inline-size:0}.hub--columns{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,20rem),1fr));align-items:start}.block{display:flex;flex-direction:column;gap:var(--hm-space-sm);min-inline-size:0}.block__title{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}ul{margin:0;padding:0;list-style:none}.link,.chip,.tile{display:inline-flex;align-items:center;gap:var(--hm-space-xs);border:1px solid transparent;border-radius:var(--hm-radius-control);background:transparent;color:var(--hm-text);font:inherit;text-align:start;cursor:pointer}.link:hover,.chip:hover,.tile:hover{background:var(--hm-hover)}.link:focus-visible,.chip:focus-visible,.tile:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:2px}@media(forced-colors:active){.link,.chip,.tile{border-color:ButtonText}.link:focus-visible,.chip:focus-visible,.tile:focus-visible{outline-color:Highlight}}.tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,13rem),1fr));gap:var(--hm-space-sm)}.tiles__cell{display:flex}.tile{flex:1 1 auto;gap:var(--hm-space-md);padding:var(--hm-space-md);border-color:var(--hm-divider);background:var(--hm-surface-raised)}.tile__icon{flex:none;color:var(--hm-accent)}.tiles--admin .tile__icon{color:var(--hm-warning)}.tile__text{display:flex;flex-direction:column;gap:var(--hm-space-nano);min-inline-size:0}.tile__label{font-size:var(--hm-font-size-md);font-weight:var(--hm-font-weight-medium);overflow-wrap:anywhere}.tile__meta{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}@media(prefers-reduced-motion:no-preference){.tiles__cell{animation:tile-in var(--hm-duration-standard) var(--hm-ease-decelerate) both;animation-delay:calc(min(var(--tile-order, 0),10)*35ms)}}@keyframes tile-in{0%{opacity:0;transform:translateY(var(--hm-space-xs))}}.chips{display:flex;flex-wrap:wrap;gap:var(--hm-space-xs)}.chip{padding:var(--hm-space-xs) var(--hm-space-sm);border-color:var(--hm-divider);background:var(--hm-surface)}.link{padding:var(--hm-space-nano) var(--hm-space-xs);margin-inline-start:calc(-1 * var(--hm-space-xs));color:var(--hm-accent)}.heart--on{color:var(--hm-danger)}\n"] }]
|
|
235
|
+
args: [{ selector: 'home', imports: [NgTemplateOutlet, DataGridComponent, DataGridCellDirective, IconButtonComponent, IconComponent, StatePageComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (noAccess()) {\n <hm-state-page kind=\"no-rights\" [heading]=\"t.noAccessTitle\" [description]=\"t.noAccessText\" action=\"\" />\n} @else {\n <div class=\"hub\" [class.hub--columns]=\"sideBySide()\">\n @if (workTiles().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-work'\">\n <h2 class=\"block__title\" [id]=\"uid + '-work'\">{{ descended() ? t.yourScreens : t.modules }}</h2>\n <ng-container *ngTemplateOutlet=\"tileGrid; context: { $implicit: workTiles(), admin: false }\" />\n </section>\n }\n\n @if (adminTiles().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-admin'\">\n <h2 class=\"block__title\" [id]=\"uid + '-admin'\">{{ t.privileged }}</h2>\n <ng-container *ngTemplateOutlet=\"tileGrid; context: { $implicit: adminTiles(), admin: true }\" />\n </section>\n }\n\n @if (recentRows().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-recent'\">\n <h2 class=\"block__title\" [id]=\"uid + '-recent'\">{{ t.recent }}</h2>\n <hm-data-grid\n [caption]=\"t.recentCaption\"\n [columns]=\"recentColumns\"\n [rows]=\"recentRows()\"\n [rowId]=\"rowId\"\n [filterable]=\"false\"\n [paginated]=\"false\"\n >\n <ng-template hmCell=\"displayName\" let-value>\n @let item = row(value);\n <button type=\"button\" class=\"link\" (click)=\"go(item.redirect)\">\n <hm-icon [shape]=\"item.icon || 'grid-layout'\" size=\"sm\" />\n <span>{{ item.displayName }}</span>\n </button>\n </ng-template>\n <ng-template hmCell=\"favorite\" let-value>\n @let item = row(value);\n <hm-icon-button\n emphasis=\"subtle\"\n icon=\"heart\"\n [solid]=\"isFavorite(item.id)\"\n [class.heart--on]=\"isFavorite(item.id)\"\n [label]=\"favoriteLabel(item)\"\n (clicked)=\"toggleFavorite(item)\"\n />\n </ng-template>\n </hm-data-grid>\n </section>\n }\n\n @if (favorites().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-fav'\">\n <h2 class=\"block__title\" [id]=\"uid + '-fav'\">{{ t.favorites }}</h2>\n <ul class=\"chips\">\n @for (fav of favorites(); track fav.id) {\n <li>\n <button type=\"button\" class=\"chip\" [title]=\"whereOf(fav)\" (click)=\"go(fav.redirect)\">\n <hm-icon [shape]=\"fav.icon || 'grid-layout'\" size=\"sm\" />\n <span>{{ fav.displayName }}</span>\n </button>\n </li>\n }\n </ul>\n </section>\n }\n\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-nav'\">\n <h2 class=\"block__title\" [id]=\"uid + '-nav'\">{{ t.navigate }}</h2>\n <ul class=\"chips\">\n @for (area of areas(); track area.routed_id) {\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"go(area.route_redirect)\">\n <hm-icon [shape]=\"area.route_icon || 'home'\" size=\"sm\" />\n <span>{{ area.route_display_name }}</span>\n </button>\n </li>\n }\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"openSettings()\">\n <hm-icon shape=\"cog\" size=\"sm\" />\n <span>{{ t.settings }}</span>\n </button>\n </li>\n </ul>\n </section>\n\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-tools'\">\n <h2 class=\"block__title\" [id]=\"uid + '-tools'\">{{ t.tools }}</h2>\n <ul class=\"chips\">\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"openDocs()\">\n <hm-icon shape=\"doc-list\" size=\"sm\" />\n <span>{{ t.systemDocs }}</span>\n </button>\n </li>\n </ul>\n </section>\n </div>\n}\n\n<ng-template #tileGrid let-tiles let-admin=\"admin\">\n <ul class=\"tiles\" [class.tiles--admin]=\"admin\">\n @for (tile of tiles; track tile.id; let i = $index) {\n <li class=\"tiles__cell\" [style.--tile-order]=\"i\">\n <button type=\"button\" class=\"tile\" (click)=\"go(tile.redirect)\">\n <hm-icon class=\"tile__icon\" [shape]=\"tile.icon ?? 'grid-layout'\" size=\"lg\" />\n <span class=\"tile__text\">\n <span class=\"tile__label\">{{ tile.label }}</span>\n @if (caption(tile); as meta) {\n <span class=\"tile__meta\">{{ meta }}</span>\n }\n </span>\n </button>\n </li>\n }\n </ul>\n</ng-template>\n", styles: [":host{display:flex;flex:1 1 auto;flex-direction:column;min-block-size:100%;min-inline-size:0}hm-state-page{flex:1 1 auto}.hub{display:flex;flex-direction:column;gap:var(--hm-space-xl);padding:var(--hm-space-lg);min-inline-size:0}.hub--columns{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,20rem),1fr));align-items:start}.block{display:flex;flex-direction:column;gap:var(--hm-space-sm);min-inline-size:0}.block__title{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}ul{margin:0;padding:0;list-style:none}.link,.chip,.tile{display:inline-flex;align-items:center;gap:var(--hm-space-xs);border:1px solid transparent;border-radius:var(--hm-radius-control);background:transparent;color:var(--hm-text);font:inherit;text-align:start;cursor:pointer}.link:hover,.chip:hover,.tile:hover{background:var(--hm-hover)}.link:focus-visible,.chip:focus-visible,.tile:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:2px}@media(forced-colors:active){.link,.chip,.tile{border-color:ButtonText}.link:focus-visible,.chip:focus-visible,.tile:focus-visible{outline-color:Highlight}}.tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,13rem),1fr));gap:var(--hm-space-sm)}.tiles__cell{display:flex}.tile{flex:1 1 auto;gap:var(--hm-space-md);padding:var(--hm-space-md);border-color:var(--hm-divider);background:var(--hm-surface-raised)}.tile__icon{flex:none;color:var(--hm-accent)}.tiles--admin .tile__icon{color:var(--hm-warning)}.tile__text{display:flex;flex-direction:column;gap:var(--hm-space-nano);min-inline-size:0}.tile__label{font-size:var(--hm-font-size-md);font-weight:var(--hm-font-weight-medium);overflow-wrap:anywhere}.tile__meta{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}@media(prefers-reduced-motion:no-preference){.tiles__cell{animation:tile-in var(--hm-duration-standard) var(--hm-ease-decelerate) both;animation-delay:calc(min(var(--tile-order, 0),10)*35ms)}}:host-context([data-motion=full]) .tiles__cell{animation:tile-in var(--hm-duration-standard) var(--hm-ease-decelerate) both;animation-delay:calc(min(var(--tile-order, 0),10)*35ms)}:host-context([data-motion=reduce]) .tiles__cell{animation:none}@keyframes tile-in{0%{opacity:0;transform:translateY(var(--hm-space-xs))}}.chips{display:flex;flex-wrap:wrap;gap:var(--hm-space-xs)}.chip{padding:var(--hm-space-xs) var(--hm-space-sm);border-color:var(--hm-divider);background:var(--hm-surface)}.link{padding:var(--hm-space-nano) var(--hm-space-xs);margin-inline-start:calc(-1 * var(--hm-space-xs));color:var(--hm-accent)}.heart--on{color:var(--hm-danger)}\n"] }]
|
|
235
236
|
}], ctorParameters: () => [] });
|
|
236
237
|
|
|
237
238
|
export { HomeAutoRedirect, HomeComponent };
|
|
238
|
-
//# sourceMappingURL=trismegisto-spine-auto-service-home.component-
|
|
239
|
+
//# sourceMappingURL=trismegisto-spine-auto-service-home.component-5_aji1hf.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trismegisto-spine-auto-service-home.component-5_aji1hf.mjs","sources":["../../../projects/ui/auto-service/src/home/home-shortcuts.ts","../../../projects/ui/auto-service/src/home/home.component.ts","../../../projects/ui/auto-service/src/home/home.component.html"],"sourcesContent":["import { Route } from '@trismegisto/spine';\nimport {\n AUTO_SERVICE_APPS_TEMPLATE_ROUTER_ID,\n AUTO_SERVICE_AUDIT_TEMPLATE_ROUTER_ID,\n AUTO_SERVICE_CLIENT_TEMPLATE_ROUTER_ID,\n AUTO_SERVICE_GROUPS_TEMPLATE_ROUTER_ID,\n AUTO_SERVICE_HOME_TEMPLATE_ROUTER_ID,\n AUTO_SERVICE_LOGIN_ATTEMPTS_TEMPLATE_ROUTER_ID,\n} from '../auto-service.tokens';\n\n/** A page the person can open, with the labels of the menus above it. */\nexport interface HomeDestination {\n readonly id: string;\n readonly label: string;\n readonly icon: string | null;\n readonly redirect: string;\n readonly trail: readonly string[];\n}\n\n/** A large entry tile on the home screen. */\nexport interface HomeShortcut extends HomeDestination {\n readonly screens: number;\n readonly privileged: boolean;\n}\n\n// Above this many tiles a grid stops being scannable at a glance.\nconst MAX_TILES = 12;\n\nconst ADMIN_TEMPLATES = new Set(\n [\n AUTO_SERVICE_GROUPS_TEMPLATE_ROUTER_ID,\n AUTO_SERVICE_APPS_TEMPLATE_ROUTER_ID,\n AUTO_SERVICE_CLIENT_TEMPLATE_ROUTER_ID,\n AUTO_SERVICE_AUDIT_TEMPLATE_ROUTER_ID,\n AUTO_SERVICE_LOGIN_ATTEMPTS_TEMPLATE_ROUTER_ID,\n ].map((id) => id.toLowerCase()),\n);\nconst HOME_TEMPLATE = AUTO_SERVICE_HOME_TEMPLATE_ROUTER_ID.toLowerCase();\n\nfunction childrenOf(node: Route): Route[] {\n return (node.routes ?? []).filter((child): child is Route => child != null);\n}\n\nfunction labelOf(node: Route): string {\n return (node.route_display_name ?? node.route_name ?? '').trim();\n}\n\nfunction identityOf(node: Route): string {\n return node.route_redirect || `${node.route_type ?? 'node'}:${node.routed_id}`;\n}\n\nfunction templateOf(node: Route): string {\n return (node.source_template_router_id ?? '').toLowerCase();\n}\n\nexport function isPage(node: Route): boolean {\n return !!node.route_redirect && (node.route_type === 'page' || childrenOf(node).length === 0);\n}\n\nexport function isHome(node: Route): boolean {\n return templateOf(node) === HOME_TEMPLATE || (isPage(node) && node.route_path === 'home');\n}\n\nexport function modules(tree: readonly Route[]): readonly Route[] {\n return tree.filter((node) => node != null && !isHome(node));\n}\n\nexport function systemAreas(tree: readonly Route[]): readonly Route[] {\n return tree.filter((node) => node != null && isHome(node));\n}\n\nexport function destinations(tree: readonly Route[], trail: readonly string[] = []): readonly HomeDestination[] {\n const found: HomeDestination[] = [];\n const walk = (nodes: readonly Route[], path: readonly string[]) => {\n for (const node of nodes) {\n if (node == null) continue;\n if (isPage(node)) {\n found.push({ id: identityOf(node), label: labelOf(node), icon: node.route_icon ?? null, redirect: node.route_redirect!, trail: path });\n }\n const kids = childrenOf(node);\n if (kids.length) walk(kids, [...path, labelOf(node)]);\n }\n };\n walk(tree, trail);\n return found;\n}\n\nexport function trailFor(tree: readonly Route[], redirect: string): readonly string[] | null {\n return destinations(tree).find((d) => d.redirect === redirect)?.trail ?? null;\n}\n\nfunction isPrivileged(node: Route): boolean {\n return ADMIN_TEMPLATES.has(templateOf(node)) || childrenOf(node).some(isPrivileged);\n}\n\ninterface Candidate {\n readonly node: Route;\n readonly trail: readonly string[];\n readonly privileged: boolean;\n}\n\nfunction navigableChildren(node: Route): Route[] {\n return childrenOf(node).filter((child) => isPage(child) || destinations([child]).length > 0);\n}\n\n/** Replaces each candidate by its navigable children; `null` when nobody has any. */\nfunction descend(level: readonly Candidate[]): Candidate[] | null {\n let changed = false;\n const next = level.flatMap((item) => {\n const kids = navigableChildren(item.node);\n if (!kids.length) return [item];\n changed = true;\n const trail = [...item.trail, labelOf(item.node)];\n return kids.map((node) => ({ node, trail, privileged: item.privileged }));\n });\n return changed ? next : null;\n}\n\nfunction toShortcut({ node, trail, privileged }: Candidate): HomeShortcut {\n const below = destinations([node]);\n return {\n id: identityOf(node),\n label: labelOf(node),\n icon: node.route_icon ?? null,\n redirect: node.route_redirect || below[0]?.redirect || '',\n trail,\n screens: below.length - (isPage(node) ? 1 : 0),\n privileged,\n };\n}\n\n/**\n * Picks the shallowest level of the tree where there is a real choice to make: a single item is\n * skipped over, a pair is opened up while the result still fits the grid, three or more stay put.\n */\nexport function entryShortcuts(tree: readonly Route[]): readonly HomeShortcut[] {\n let level: readonly Candidate[] = modules(tree).map((node) => ({ node, trail: [], privileged: isPrivileged(node) }));\n while (level.length > 0 && level.length < 3) {\n const next = descend(level);\n if (!next) break;\n if (level.length === 2 && next.length > MAX_TILES) break;\n level = next;\n }\n return level.map(toShortcut);\n}\n","import { ChangeDetectionStrategy, Component, Injectable, computed, effect, inject } from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { Router } from '@angular/router';\nimport { ViewService } from '@orfeu/angular/core';\nimport {\n DataGridCellDirective,\n DataGridColumn,\n DataGridComponent,\n IconButtonComponent,\n IconComponent,\n StatePageComponent,\n} from '@trismegisto/angular';\nimport { formatMessage, pluralMessage } from '@trismegisto/primitives';\nimport { QuickAccessItem, Route, SPINE_MESSAGES, SpineQuickAccessService, spineRoutesOf } from '@trismegisto/spine';\nimport { SpineDocsService, SpineSettingsService } from '@trismegisto/spine/layout';\nimport { map } from 'rxjs';\nimport { HomeShortcut, destinations, entryShortcuts, modules, systemAreas, trailFor } from './home-shortcuts';\n\n/** Remembers, for the whole app load, that the home screen already jumped to the only screen. */\n@Injectable({ providedIn: 'root' })\nexport class HomeAutoRedirect {\n done = false;\n}\n\ninterface RecentRow extends QuickAccessItem {\n readonly where: string;\n}\n\nconst TRAIL_SEPARATOR = ' › ';\nconst RECENT_ROWS = 4;\n\nlet instances = 0;\n\nfunction cleanTree(view: Parameters<typeof spineRoutesOf>[0]): Route[] {\n return spineRoutesOf(view).filter((node): node is Route => node != null);\n}\n\n@Component({\n selector: 'home',\n imports: [NgTemplateOutlet, DataGridComponent, DataGridCellDirective, IconButtonComponent, IconComponent, StatePageComponent],\n templateUrl: './home.component.html',\n styleUrl: './home.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class HomeComponent {\n private readonly router = inject(Router);\n private readonly quickAccess = inject(SpineQuickAccessService);\n private readonly docs = inject(SpineDocsService);\n private readonly settings = inject(SpineSettingsService);\n private readonly autoRedirect = inject(HomeAutoRedirect);\n private readonly messages = inject(SPINE_MESSAGES);\n protected readonly t = this.messages.home;\n protected readonly uid = `home-${++instances}`;\n\n private readonly view = inject(ViewService);\n private readonly tree = toSignal(this.view.currentView$.pipe(map(cleanTree)), {\n initialValue: cleanTree(this.view.currentView),\n });\n\n private readonly workDestinations = computed(() => destinations(modules(this.tree())));\n protected readonly noAccess = computed(() => this.tree().length > 0 && modules(this.tree()).length === 0);\n protected readonly areas = computed(() => systemAreas(this.tree()));\n\n private readonly shortcuts = computed(() => entryShortcuts(this.tree()));\n protected readonly workTiles = computed(() => this.shortcuts().filter((s) => !s.privileged));\n protected readonly adminTiles = computed(() => this.shortcuts().filter((s) => s.privileged));\n protected readonly descended = computed(() => this.shortcuts().some((s) => s.trail.length > 0));\n protected readonly sideBySide = computed(() => this.shortcuts().length <= 2);\n\n /** Every tile already opens a final screen, so recents/favorites would not save a single click. */\n private readonly everythingInView = computed(() => {\n const list = this.shortcuts();\n return list.length > 0 && list.every((s) => s.screens === 0);\n });\n\n protected readonly favorites = computed(() => (this.everythingInView() ? [] : this.quickAccess.favorites()));\n protected readonly recentRows = computed<readonly RecentRow[]>(() => {\n if (this.everythingInView()) return [];\n return this.quickAccess\n .recents()\n .slice(0, RECENT_ROWS)\n .map((item) => ({ ...item, where: this.whereOf(item) }));\n });\n\n protected readonly recentColumns: readonly DataGridColumn<RecentRow>[] = [\n { key: 'displayName', title: this.t.colScreen, value: (row) => row.displayName },\n { key: 'where', title: this.t.colWhere, value: (row) => row.where },\n {\n key: 'favorite',\n title: this.t.colFavorite,\n value: () => '',\n filterable: false,\n sortable: false,\n searchable: false,\n initialWidth: '5.5rem',\n },\n ];\n protected readonly rowId = (row: RecentRow) => row.id;\n\n constructor() {\n effect(() => {\n const only = this.workDestinations();\n if (only.length !== 1 || this.autoRedirect.done) return;\n this.autoRedirect.done = true;\n void this.router.navigateByUrl(only[0].redirect, { replaceUrl: true });\n });\n }\n\n protected caption(tile: HomeShortcut): string {\n if (tile.screens > 0) return pluralMessage(this.t.screenCount, tile.screens, this.messages.meta.locale);\n return tile.trail.join(TRAIL_SEPARATOR);\n }\n\n protected whereOf(item: QuickAccessItem): string {\n const trail = item.trail ?? trailFor(this.tree(), item.redirect);\n return trail?.join(TRAIL_SEPARATOR) || this.t.noModule;\n }\n\n protected row(value: unknown): RecentRow {\n return value as RecentRow;\n }\n\n protected isFavorite(id: string): boolean {\n return this.quickAccess.isFavorite(id);\n }\n\n protected favoriteLabel(row: RecentRow): string {\n const template = this.isFavorite(row.id) ? this.t.unfavorite : this.t.favorite;\n return formatMessage(template, { name: row.displayName });\n }\n\n protected toggleFavorite(row: RecentRow): void {\n this.quickAccess.toggleFavorite({\n id: row.id,\n icon: row.icon,\n displayName: row.displayName,\n redirect: row.redirect,\n trail: row.trail ?? trailFor(this.tree(), row.redirect) ?? [],\n });\n }\n\n protected go(redirect: string | null | undefined): void {\n if (redirect) void this.router.navigateByUrl(redirect);\n }\n\n protected openSettings(): void {\n this.settings.open();\n }\n\n protected openDocs(): void {\n this.docs.openIndex();\n }\n}\n","@if (noAccess()) {\n <hm-state-page kind=\"no-rights\" [heading]=\"t.noAccessTitle\" [description]=\"t.noAccessText\" action=\"\" />\n} @else {\n <div class=\"hub\" [class.hub--columns]=\"sideBySide()\">\n @if (workTiles().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-work'\">\n <h2 class=\"block__title\" [id]=\"uid + '-work'\">{{ descended() ? t.yourScreens : t.modules }}</h2>\n <ng-container *ngTemplateOutlet=\"tileGrid; context: { $implicit: workTiles(), admin: false }\" />\n </section>\n }\n\n @if (adminTiles().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-admin'\">\n <h2 class=\"block__title\" [id]=\"uid + '-admin'\">{{ t.privileged }}</h2>\n <ng-container *ngTemplateOutlet=\"tileGrid; context: { $implicit: adminTiles(), admin: true }\" />\n </section>\n }\n\n @if (recentRows().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-recent'\">\n <h2 class=\"block__title\" [id]=\"uid + '-recent'\">{{ t.recent }}</h2>\n <hm-data-grid\n [caption]=\"t.recentCaption\"\n [columns]=\"recentColumns\"\n [rows]=\"recentRows()\"\n [rowId]=\"rowId\"\n [filterable]=\"false\"\n [paginated]=\"false\"\n >\n <ng-template hmCell=\"displayName\" let-value>\n @let item = row(value);\n <button type=\"button\" class=\"link\" (click)=\"go(item.redirect)\">\n <hm-icon [shape]=\"item.icon || 'grid-layout'\" size=\"sm\" />\n <span>{{ item.displayName }}</span>\n </button>\n </ng-template>\n <ng-template hmCell=\"favorite\" let-value>\n @let item = row(value);\n <hm-icon-button\n emphasis=\"subtle\"\n icon=\"heart\"\n [solid]=\"isFavorite(item.id)\"\n [class.heart--on]=\"isFavorite(item.id)\"\n [label]=\"favoriteLabel(item)\"\n (clicked)=\"toggleFavorite(item)\"\n />\n </ng-template>\n </hm-data-grid>\n </section>\n }\n\n @if (favorites().length) {\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-fav'\">\n <h2 class=\"block__title\" [id]=\"uid + '-fav'\">{{ t.favorites }}</h2>\n <ul class=\"chips\">\n @for (fav of favorites(); track fav.id) {\n <li>\n <button type=\"button\" class=\"chip\" [title]=\"whereOf(fav)\" (click)=\"go(fav.redirect)\">\n <hm-icon [shape]=\"fav.icon || 'grid-layout'\" size=\"sm\" />\n <span>{{ fav.displayName }}</span>\n </button>\n </li>\n }\n </ul>\n </section>\n }\n\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-nav'\">\n <h2 class=\"block__title\" [id]=\"uid + '-nav'\">{{ t.navigate }}</h2>\n <ul class=\"chips\">\n @for (area of areas(); track area.routed_id) {\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"go(area.route_redirect)\">\n <hm-icon [shape]=\"area.route_icon || 'home'\" size=\"sm\" />\n <span>{{ area.route_display_name }}</span>\n </button>\n </li>\n }\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"openSettings()\">\n <hm-icon shape=\"cog\" size=\"sm\" />\n <span>{{ t.settings }}</span>\n </button>\n </li>\n </ul>\n </section>\n\n <section class=\"block\" [attr.aria-labelledby]=\"uid + '-tools'\">\n <h2 class=\"block__title\" [id]=\"uid + '-tools'\">{{ t.tools }}</h2>\n <ul class=\"chips\">\n <li>\n <button type=\"button\" class=\"chip\" (click)=\"openDocs()\">\n <hm-icon shape=\"doc-list\" size=\"sm\" />\n <span>{{ t.systemDocs }}</span>\n </button>\n </li>\n </ul>\n </section>\n </div>\n}\n\n<ng-template #tileGrid let-tiles let-admin=\"admin\">\n <ul class=\"tiles\" [class.tiles--admin]=\"admin\">\n @for (tile of tiles; track tile.id; let i = $index) {\n <li class=\"tiles__cell\" [style.--tile-order]=\"i\">\n <button type=\"button\" class=\"tile\" (click)=\"go(tile.redirect)\">\n <hm-icon class=\"tile__icon\" [shape]=\"tile.icon ?? 'grid-layout'\" size=\"lg\" />\n <span class=\"tile__text\">\n <span class=\"tile__label\">{{ tile.label }}</span>\n @if (caption(tile); as meta) {\n <span class=\"tile__meta\">{{ meta }}</span>\n }\n </span>\n </button>\n </li>\n }\n </ul>\n</ng-template>\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Especificação técnica da tela Tentativas de login (menu Auditoria) do auto-serviço (`RouteDocument.loadTechnical`),
|
|
2
|
+
// em Markdown. Módulo próprio, carregado só pelo `import()` de `AUTO_SERVICE_DOCUMENTS`: vira um chunk à parte no FESM
|
|
3
|
+
// e fica fora do bundle inicial. Não importe estaticamente nem reexporte no `public-api.ts`.
|
|
4
|
+
var loginAttempts_technical = `# Tentativas de login — especificação técnica
|
|
5
|
+
|
|
6
|
+
Tela \`AutoServiceLoginAttemptsComponent\`, ligada ao router de template
|
|
7
|
+
\`AUTO_SERVICE_LOGIN_ATTEMPTS_TEMPLATE_ROUTER_ID\` (\`ad100005-…\`, página \`administration-login-attempts\` no menu
|
|
8
|
+
\`administration-audit\`): o seletor de client + \`SpineLoginAttemptsComponent\` (\`@trismegisto/spine/audit\`) com
|
|
9
|
+
\`scope="client"\`. No painel central do Argos a mesma tela usa \`scope="admin"\` (todos os realms, colunas Realm e
|
|
10
|
+
Client).
|
|
11
|
+
|
|
12
|
+
## Endpoints
|
|
13
|
+
|
|
14
|
+
| Ação | Endpoint |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| Clients que a pessoa administra | \`GET /auto-service\` |
|
|
17
|
+
| Tentativas (filtros, página e total no servidor) | \`GET /auto-service/:clientId/attempts\` |
|
|
18
|
+
|
|
19
|
+
Query: \`from\`/\`to\` (ISO; o período escolhido vira [início do primeiro dia, início do dia seguinte ao último) no
|
|
20
|
+
fuso do navegador), \`user\`, \`ip\` (IP ou CIDR), \`result\`, \`method\`, \`reason\`, \`page\`, \`limit\` (20, 50 ou
|
|
21
|
+
100). Resposta: \`{ items, total, page, limit }\`, mais recentes primeiro.
|
|
22
|
+
|
|
23
|
+
## Regras
|
|
24
|
+
|
|
25
|
+
- **Autorização** (\`clientAdminGuard\`): grupo de administração do projeto do client + crachá Administrador ativo. O
|
|
26
|
+
servidor força \`client_id = :clientId\` (o da query é ignorado) e não aceita filtro de realm; 403 vira a mensagem de
|
|
27
|
+
falta de acesso.
|
|
28
|
+
- Filtro inválido (\`invalid_filter\`) ou janela acima de 366 dias (\`range_too_large\`) mostram o detalhe do servidor.
|
|
29
|
+
- Busca por usuário e IP aplicam 350 ms depois de parar de digitar; os outros filtros, na hora. Só a resposta da
|
|
30
|
+
consulta mais recente é usada.
|
|
31
|
+
- Motivo e método desconhecidos aparecem com o código que veio do banco (\`auth.auth_attempts.reason\`/\`method\`).
|
|
32
|
+
- A tela fica no ponto \`@trismegisto/spine/audit\`, importado só pelo chunk lazy desta página (o \`check-lazy\` do
|
|
33
|
+
build garante).
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
export { loginAttempts_technical as default };
|
|
37
|
+
//# sourceMappingURL=trismegisto-spine-auto-service-login-attempts.technical-CasKIIws.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trismegisto-spine-auto-service-login-attempts.technical-CasKIIws.mjs","sources":["../../../projects/ui/auto-service/src/docs/login-attempts.technical.ts"],"sourcesContent":["// Especificação técnica da tela Tentativas de login (menu Auditoria) do auto-serviço (`RouteDocument.loadTechnical`),\n// em Markdown. Módulo próprio, carregado só pelo `import()` de `AUTO_SERVICE_DOCUMENTS`: vira um chunk à parte no FESM\n// e fica fora do bundle inicial. Não importe estaticamente nem reexporte no `public-api.ts`.\nexport default `# Tentativas de login — especificação técnica\n\nTela \\`AutoServiceLoginAttemptsComponent\\`, ligada ao router de template\n\\`AUTO_SERVICE_LOGIN_ATTEMPTS_TEMPLATE_ROUTER_ID\\` (\\`ad100005-…\\`, página \\`administration-login-attempts\\` no menu\n\\`administration-audit\\`): o seletor de client + \\`SpineLoginAttemptsComponent\\` (\\`@trismegisto/spine/audit\\`) com\n\\`scope=\"client\"\\`. No painel central do Argos a mesma tela usa \\`scope=\"admin\"\\` (todos os realms, colunas Realm e\nClient).\n\n## Endpoints\n\n| Ação | Endpoint |\n| --- | --- |\n| Clients que a pessoa administra | \\`GET /auto-service\\` |\n| Tentativas (filtros, página e total no servidor) | \\`GET /auto-service/:clientId/attempts\\` |\n\nQuery: \\`from\\`/\\`to\\` (ISO; o período escolhido vira [início do primeiro dia, início do dia seguinte ao último) no\nfuso do navegador), \\`user\\`, \\`ip\\` (IP ou CIDR), \\`result\\`, \\`method\\`, \\`reason\\`, \\`page\\`, \\`limit\\` (20, 50 ou\n100). Resposta: \\`{ items, total, page, limit }\\`, mais recentes primeiro.\n\n## Regras\n\n- **Autorização** (\\`clientAdminGuard\\`): grupo de administração do projeto do client + crachá Administrador ativo. O\n servidor força \\`client_id = :clientId\\` (o da query é ignorado) e não aceita filtro de realm; 403 vira a mensagem de\n falta de acesso.\n- Filtro inválido (\\`invalid_filter\\`) ou janela acima de 366 dias (\\`range_too_large\\`) mostram o detalhe do servidor.\n- Busca por usuário e IP aplicam 350 ms depois de parar de digitar; os outros filtros, na hora. Só a resposta da\n consulta mais recente é usada.\n- Motivo e método desconhecidos aparecem com o código que veio do banco (\\`auth.auth_attempts.reason\\`/\\`method\\`).\n- A tela fica no ponto \\`@trismegisto/spine/audit\\`, importado só pelo chunk lazy desta página (o \\`check-lazy\\` do\n build garante).\n`;\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA,8BAAe,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8Bd;;"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Manual de uso da tela Tentativas de login (menu Auditoria) do auto-serviço (`RouteDocument.load`), em Markdown.
|
|
2
|
+
// Módulo próprio, carregado só pelo `import()` de `AUTO_SERVICE_DOCUMENTS`: vira um chunk à parte no FESM e fica fora
|
|
3
|
+
// do bundle inicial. Não importe estaticamente nem reexporte no `public-api.ts`.
|
|
4
|
+
var loginAttempts_usage = `# Tentativas de login
|
|
5
|
+
|
|
6
|
+
Cada tentativa de entrar nesta aplicação: quando foi, quem tentou, de qual IP e navegador, por qual método e com que
|
|
7
|
+
resultado. Nas falhas, mostra também o motivo. Fica no menu **Auditoria**, ao lado de **Ações**. Serve para
|
|
8
|
+
investigar acessos suspeitos, contas bloqueadas e erros de login relatados por quem usa a aplicação. A tela é só de
|
|
9
|
+
consulta.
|
|
10
|
+
|
|
11
|
+
## Antes de começar
|
|
12
|
+
|
|
13
|
+
Como nas outras telas do auto-serviço, o client é o do app em que você está logado. É preciso estar no grupo de
|
|
14
|
+
administração do projeto desse app e com o crachá **Administrador** ativo. Você só vê as tentativas de login feitas
|
|
15
|
+
nesta aplicação, não as de outras aplicações do Argos.
|
|
16
|
+
|
|
17
|
+
## Como usar
|
|
18
|
+
|
|
19
|
+
1. **Usuário**: procura no username, no e-mail e no nome, e também no que foi digitado na tela de login, mesmo que
|
|
20
|
+
não exista usuário com esse nome. Aplica ao parar de digitar ou com Enter.
|
|
21
|
+
2. **Período**: de um dia a outro, ou um dos atalhos (hoje, últimos 7 dias, últimos 30 dias, este mês, mês passado).
|
|
22
|
+
Ao abrir a tela, são os últimos 7 dias. Cada consulta cobre no máximo 366 dias.
|
|
23
|
+
3. **Resultado** (Sucesso, Falha ou Cancelada) e **Método** (Senha, MFA ou o provedor externo: GitHub, Google,
|
|
24
|
+
Keycloak, Entra ID).
|
|
25
|
+
4. **Filtros avançados** abre **IP ou rede** (um IP, como \`10.0.0.5\`, ou uma rede, como \`10.0.0.0/8\`) e **Motivo**.
|
|
26
|
+
5. **Limpar** volta tudo ao padrão; **Atualizar** recarrega na hora.
|
|
27
|
+
6. A tabela lista as tentativas da mais recente para a mais antiga: **Data/hora** (no fuso do seu navegador),
|
|
28
|
+
**Resultado**, **Método**, **Usuário**, **Motivo**, **IP** e **Navegador** (coluna que pode ser escondida). Escolha
|
|
29
|
+
quantas linhas por página no rodapé.
|
|
30
|
+
7. Nas ações de cada linha, **Filtrar por este IP** e **Filtrar por este usuário** aplicam o filtro na hora.
|
|
31
|
+
|
|
32
|
+
## Usuário
|
|
33
|
+
|
|
34
|
+
Quando a tentativa é de um usuário cadastrado, aparecem o nome e, abaixo, o username e o e-mail. Quando o que foi
|
|
35
|
+
digitado não corresponde a ninguém, aparece o texto digitado com a nota **sem usuário correspondente**: pode ser
|
|
36
|
+
erro de digitação ou alguém tentando nomes ao acaso.
|
|
37
|
+
|
|
38
|
+
## Motivos de falha
|
|
39
|
+
|
|
40
|
+
| Motivo | O que significa |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| Senha incorreta | O usuário existe, a senha não confere. |
|
|
43
|
+
| Usuário inexistente ou inativo | Ninguém com esse identificador, ou a conta está desativada. |
|
|
44
|
+
| Conta bloqueada por tentativas | Falhas demais seguidas; a conta fica bloqueada por um tempo. |
|
|
45
|
+
| Código de verificação incorreto / vencido | O código do MFA não confere ou passou da validade. |
|
|
46
|
+
| Método de verificação não permitido / desconhecido | O MFA escolhido não vale para essa conta. |
|
|
47
|
+
| Código de recuperação incorreto ou já usado / recusado | Problema com um código de recuperação do MFA. |
|
|
48
|
+
| Conta externa não vinculada | Login por provedor externo sem conta correspondente no Argos. |
|
|
49
|
+
| Usuário de outro realm | A conta existe, mas em outro realm. |
|
|
50
|
+
|
|
51
|
+
## Quando algo não carrega
|
|
52
|
+
|
|
53
|
+
- **Só o grupo de administração do projeto vê as tentativas:** sua conta não administra este client, ou o crachá
|
|
54
|
+
Administrador não está ativo.
|
|
55
|
+
- **Não foi possível carregar as tentativas:** a mensagem traz o motivo (por exemplo, um IP inválido no filtro);
|
|
56
|
+
corrija o filtro ou use **Atualizar**.
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
export { loginAttempts_usage as default };
|
|
60
|
+
//# sourceMappingURL=trismegisto-spine-auto-service-login-attempts.usage-BMgLNu7W.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trismegisto-spine-auto-service-login-attempts.usage-BMgLNu7W.mjs","sources":["../../../projects/ui/auto-service/src/docs/login-attempts.usage.ts"],"sourcesContent":["// Manual de uso da tela Tentativas de login (menu Auditoria) do auto-serviço (`RouteDocument.load`), em Markdown.\n// Módulo próprio, carregado só pelo `import()` de `AUTO_SERVICE_DOCUMENTS`: vira um chunk à parte no FESM e fica fora\n// do bundle inicial. Não importe estaticamente nem reexporte no `public-api.ts`.\nexport default `# Tentativas de login\n\nCada tentativa de entrar nesta aplicação: quando foi, quem tentou, de qual IP e navegador, por qual método e com que\nresultado. Nas falhas, mostra também o motivo. Fica no menu **Auditoria**, ao lado de **Ações**. Serve para\ninvestigar acessos suspeitos, contas bloqueadas e erros de login relatados por quem usa a aplicação. A tela é só de\nconsulta.\n\n## Antes de começar\n\nComo nas outras telas do auto-serviço, o client é o do app em que você está logado. É preciso estar no grupo de\nadministração do projeto desse app e com o crachá **Administrador** ativo. Você só vê as tentativas de login feitas\nnesta aplicação, não as de outras aplicações do Argos.\n\n## Como usar\n\n1. **Usuário**: procura no username, no e-mail e no nome, e também no que foi digitado na tela de login, mesmo que\n não exista usuário com esse nome. Aplica ao parar de digitar ou com Enter.\n2. **Período**: de um dia a outro, ou um dos atalhos (hoje, últimos 7 dias, últimos 30 dias, este mês, mês passado).\n Ao abrir a tela, são os últimos 7 dias. Cada consulta cobre no máximo 366 dias.\n3. **Resultado** (Sucesso, Falha ou Cancelada) e **Método** (Senha, MFA ou o provedor externo: GitHub, Google,\n Keycloak, Entra ID).\n4. **Filtros avançados** abre **IP ou rede** (um IP, como \\`10.0.0.5\\`, ou uma rede, como \\`10.0.0.0/8\\`) e **Motivo**.\n5. **Limpar** volta tudo ao padrão; **Atualizar** recarrega na hora.\n6. A tabela lista as tentativas da mais recente para a mais antiga: **Data/hora** (no fuso do seu navegador),\n **Resultado**, **Método**, **Usuário**, **Motivo**, **IP** e **Navegador** (coluna que pode ser escondida). Escolha\n quantas linhas por página no rodapé.\n7. Nas ações de cada linha, **Filtrar por este IP** e **Filtrar por este usuário** aplicam o filtro na hora.\n\n## Usuário\n\nQuando a tentativa é de um usuário cadastrado, aparecem o nome e, abaixo, o username e o e-mail. Quando o que foi\ndigitado não corresponde a ninguém, aparece o texto digitado com a nota **sem usuário correspondente**: pode ser\nerro de digitação ou alguém tentando nomes ao acaso.\n\n## Motivos de falha\n\n| Motivo | O que significa |\n| --- | --- |\n| Senha incorreta | O usuário existe, a senha não confere. |\n| Usuário inexistente ou inativo | Ninguém com esse identificador, ou a conta está desativada. |\n| Conta bloqueada por tentativas | Falhas demais seguidas; a conta fica bloqueada por um tempo. |\n| Código de verificação incorreto / vencido | O código do MFA não confere ou passou da validade. |\n| Método de verificação não permitido / desconhecido | O MFA escolhido não vale para essa conta. |\n| Código de recuperação incorreto ou já usado / recusado | Problema com um código de recuperação do MFA. |\n| Conta externa não vinculada | Login por provedor externo sem conta correspondente no Argos. |\n| Usuário de outro realm | A conta existe, mas em outro realm. |\n\n## Quando algo não carrega\n\n- **Só o grupo de administração do projeto vê as tentativas:** sua conta não administra este client, ou o crachá\n Administrador não está ativo.\n- **Não foi possível carregar as tentativas:** a mensagem traz o motivo (por exemplo, um IP inválido no filtro);\n corrija o filtro ou use **Atualizar**.\n`;\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA,0BAAe,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDd;;"}
|
|
@@ -9,41 +9,42 @@ import { urlPath } from '@trismegisto/primitives';
|
|
|
9
9
|
import * as i2 from '@orfeu/angular/core';
|
|
10
10
|
|
|
11
11
|
const AUTO_SERVICE_CONFIG = new InjectionToken('AUTO_SERVICE_CONFIG');
|
|
12
|
-
// router_id dos
|
|
13
|
-
// ver apps/argos/database/
|
|
12
|
+
// router_id dos roteadores (páginas) do template "auto-service" (web.template_router,
|
|
13
|
+
// ver apps/argos/database/seed/04-web.template_router.sql) -- valores literais e
|
|
14
14
|
// estáveis (mesma convenção do resto do seed: UUID fixo, nunca gerado em runtime). É
|
|
15
15
|
// contra ISSO que o ComponentDistributorService casa via source_template_router_id
|
|
16
16
|
// (não o router_id materializado de cada client, que varia): qualquer client
|
|
17
|
-
// vinculado ao template "auto-service" resolve
|
|
18
|
-
// declarar nada no próprio features.routes.ts. As
|
|
19
|
-
// "Administração"
|
|
20
|
-
// page_id de
|
|
17
|
+
// vinculado ao template "auto-service" resolve todos automaticamente, sem precisar
|
|
18
|
+
// declarar nada no próprio features.routes.ts. As páginas vivem dentro da navegação
|
|
19
|
+
// "Administração" do template (Ações e Tentativas de login num menu "Auditoria" dentro
|
|
20
|
+
// dela) -- navegação e menu nunca precisam de componente (nunca são page_id de router).
|
|
21
21
|
const AUTO_SERVICE_GROUPS_TEMPLATE_ROUTER_ID = 'ad100001-0000-4000-8000-000000000001';
|
|
22
22
|
const AUTO_SERVICE_APPS_TEMPLATE_ROUTER_ID = 'ad100002-0000-4000-8000-000000000001';
|
|
23
23
|
const AUTO_SERVICE_CLIENT_TEMPLATE_ROUTER_ID = 'ad100003-0000-4000-8000-000000000001';
|
|
24
|
-
// Auditoria
|
|
25
|
-
//
|
|
26
|
-
// (mesmos guards da página de Aplicações). Até lá a tela também abre pela aba "Auditoria" da própria
|
|
27
|
-
// página de Aplicações -- mesma autorização, por construção.
|
|
24
|
+
// Menu "Auditoria" (rota AD000006, dentro de "Administração", migration 0012 do argos): duas páginas.
|
|
25
|
+
// Ações (a leitura da auditoria, `audit.events`) -- o nome da constante ficou do tempo em que era a página "Auditoria".
|
|
28
26
|
const AUTO_SERVICE_AUDIT_TEMPLATE_ROUTER_ID = 'ad100004-0000-4000-8000-000000000001';
|
|
27
|
+
// Tentativas de login (`auth.auth_attempts` com o client do app).
|
|
28
|
+
const AUTO_SERVICE_LOGIN_ATTEMPTS_TEMPLATE_ROUTER_ID = 'ad100005-0000-4000-8000-000000000001';
|
|
29
29
|
// Início ("home", página de topo e pública do mesmo template). Todo client clonado do
|
|
30
30
|
// template ganha a própria linha em web.router com source_template_router_id apontando pra cá.
|
|
31
31
|
const AUTO_SERVICE_HOME_TEMPLATE_ROUTER_ID = '6a9a13db-f31b-494e-b51c-79af0ac28b60';
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* As
|
|
34
|
+
* As 6 telas do auto-serviço, carregadas sob demanda. Cada `import()` relativo vira um chunk próprio no
|
|
35
35
|
* FESM publicado (`trismegisto-spine-auto-service-<tela>-<hash>.mjs`), que o bundler do app mantém fora
|
|
36
36
|
* do carregamento inicial. Não exporte as telas no `public-api.ts` (ver o comentário lá).
|
|
37
37
|
*/
|
|
38
38
|
const AUTO_SERVICE_SCREENS = {
|
|
39
|
-
home: () => import('./trismegisto-spine-auto-service-home.component-
|
|
40
|
-
groups: () => import('./trismegisto-spine-auto-service-auto-service-groups.component-
|
|
41
|
-
apps: () => import('./trismegisto-spine-auto-service-auto-service.component-
|
|
42
|
-
client: () => import('./trismegisto-spine-auto-service-auto-service-client.component-
|
|
43
|
-
audit: () => import('./trismegisto-spine-auto-service-auto-service-audit.component-
|
|
39
|
+
home: () => import('./trismegisto-spine-auto-service-home.component-5_aji1hf.mjs').then(m => m.HomeComponent),
|
|
40
|
+
groups: () => import('./trismegisto-spine-auto-service-auto-service-groups.component-UVMGF7-B.mjs').then(m => m.AutoServiceGroupsComponent),
|
|
41
|
+
apps: () => import('./trismegisto-spine-auto-service-auto-service.component-DmLafUkX.mjs').then(m => m.AutoServiceComponent),
|
|
42
|
+
client: () => import('./trismegisto-spine-auto-service-auto-service-client.component-DAxhZC-a.mjs').then(m => m.AutoServiceClientComponent),
|
|
43
|
+
audit: () => import('./trismegisto-spine-auto-service-auto-service-audit.component-BIL8tH1I.mjs').then(m => m.AutoServiceAuditComponent),
|
|
44
|
+
loginAttempts: () => import('./trismegisto-spine-auto-service-auto-service-login-attempts.component-Boi-kG0G.mjs').then(m => m.AutoServiceLoginAttemptsComponent),
|
|
44
45
|
};
|
|
45
46
|
/**
|
|
46
|
-
* Documentação das
|
|
47
|
+
* Documentação das 6 telas (botão de documentação da barra da página e índice "Documentação do sistema"): o manual
|
|
47
48
|
* de uso (`load`) e a especificação técnica (`loadTechnical`, só com `SpineConfig.technicalDocs`). O Markdown fica em
|
|
48
49
|
* módulos `.ts` próprios em `./docs/` (o ng-packagr não carrega `.md` como texto), cada um atrás de um `import()`
|
|
49
50
|
* relativo: vira um chunk à parte no FESM e só é baixado quando alguém abre o documento. Sem `title`/`group`: o
|
|
@@ -56,20 +57,24 @@ const AUTO_SERVICE_DOCUMENTS = {
|
|
|
56
57
|
loadTechnical: () => import('./trismegisto-spine-auto-service-home.technical-BrVhcgDO.mjs').then(m => m.default),
|
|
57
58
|
},
|
|
58
59
|
groups: {
|
|
59
|
-
load: () => import('./trismegisto-spine-auto-service-groups.usage-
|
|
60
|
-
loadTechnical: () => import('./trismegisto-spine-auto-service-groups.technical-
|
|
60
|
+
load: () => import('./trismegisto-spine-auto-service-groups.usage-Cz5sCKgn.mjs').then(m => m.default),
|
|
61
|
+
loadTechnical: () => import('./trismegisto-spine-auto-service-groups.technical-BlktCM70.mjs').then(m => m.default),
|
|
61
62
|
},
|
|
62
63
|
apps: {
|
|
63
|
-
load: () => import('./trismegisto-spine-auto-service-apps.usage-
|
|
64
|
-
loadTechnical: () => import('./trismegisto-spine-auto-service-apps.technical-
|
|
64
|
+
load: () => import('./trismegisto-spine-auto-service-apps.usage-CAXMP5r8.mjs').then(m => m.default),
|
|
65
|
+
loadTechnical: () => import('./trismegisto-spine-auto-service-apps.technical-BAKdkVdz.mjs').then(m => m.default),
|
|
65
66
|
},
|
|
66
67
|
client: {
|
|
67
|
-
load: () => import('./trismegisto-spine-auto-service-client.usage-
|
|
68
|
-
loadTechnical: () => import('./trismegisto-spine-auto-service-client.technical-
|
|
68
|
+
load: () => import('./trismegisto-spine-auto-service-client.usage-CcJANb20.mjs').then(m => m.default),
|
|
69
|
+
loadTechnical: () => import('./trismegisto-spine-auto-service-client.technical-DP5G8i3o.mjs').then(m => m.default),
|
|
69
70
|
},
|
|
70
71
|
audit: {
|
|
71
|
-
load: () => import('./trismegisto-spine-auto-service-audit.usage-
|
|
72
|
-
loadTechnical: () => import('./trismegisto-spine-auto-service-audit.technical-
|
|
72
|
+
load: () => import('./trismegisto-spine-auto-service-audit.usage-IdXMruv5.mjs').then(m => m.default),
|
|
73
|
+
loadTechnical: () => import('./trismegisto-spine-auto-service-audit.technical-B75d4mh2.mjs').then(m => m.default),
|
|
74
|
+
},
|
|
75
|
+
loginAttempts: {
|
|
76
|
+
load: () => import('./trismegisto-spine-auto-service-login-attempts.usage-BMgLNu7W.mjs').then(m => m.default),
|
|
77
|
+
loadTechnical: () => import('./trismegisto-spine-auto-service-login-attempts.technical-CasKIIws.mjs').then(m => m.default),
|
|
73
78
|
},
|
|
74
79
|
};
|
|
75
80
|
// Cada app consumidora chama isso uma vez no bootstrap (mesmo padrão do
|
|
@@ -79,7 +84,7 @@ const AUTO_SERVICE_DOCUMENTS = {
|
|
|
79
84
|
// route_id -- esse varia por client, ver ComponentDistributorService). O app não
|
|
80
85
|
// precisa (e não deve) declarar nada em features.routes.ts pra isso funcionar; basta
|
|
81
86
|
// o client dele estar vinculado ao template "auto-service" (auth.clients.template_id).
|
|
82
|
-
//
|
|
87
|
+
// 6 páginas (Início + Grupos/Aplicações/Cliente + Ações/Tentativas de login no menu Auditoria) -- o ComponentDistributorService
|
|
83
88
|
// já suporta N entradas, cada uma casando pelo seu próprio source_template_router_id.
|
|
84
89
|
function provideAutoService(config) {
|
|
85
90
|
const componentRoutes = [
|
|
@@ -88,6 +93,7 @@ function provideAutoService(config) {
|
|
|
88
93
|
{ source_template_router_id: AUTO_SERVICE_APPS_TEMPLATE_ROUTER_ID, load: AUTO_SERVICE_SCREENS.apps, document: AUTO_SERVICE_DOCUMENTS.apps },
|
|
89
94
|
{ source_template_router_id: AUTO_SERVICE_CLIENT_TEMPLATE_ROUTER_ID, load: AUTO_SERVICE_SCREENS.client, document: AUTO_SERVICE_DOCUMENTS.client },
|
|
90
95
|
{ source_template_router_id: AUTO_SERVICE_AUDIT_TEMPLATE_ROUTER_ID, load: AUTO_SERVICE_SCREENS.audit, document: AUTO_SERVICE_DOCUMENTS.audit },
|
|
96
|
+
{ source_template_router_id: AUTO_SERVICE_LOGIN_ATTEMPTS_TEMPLATE_ROUTER_ID, load: AUTO_SERVICE_SCREENS.loginAttempts, document: AUTO_SERVICE_DOCUMENTS.loginAttempts },
|
|
91
97
|
];
|
|
92
98
|
return makeEnvironmentProviders([
|
|
93
99
|
{ provide: AUTO_SERVICE_CONFIG, useValue: config },
|
|
@@ -133,8 +139,13 @@ class AutoServiceService {
|
|
|
133
139
|
* em todos os seus routers. Vale também para routers de template (ordem é do client).
|
|
134
140
|
*/
|
|
135
141
|
reorderRouters(clientId, groups) { return firstValueFrom(this.http.patch(this.url `/${clientId}/routers/reorder`, { groups }, { headers: this.headers })); }
|
|
136
|
-
// Groups
|
|
142
|
+
// Groups (do projeto do client, sem o grupo de administração: editar/excluir/membros dele respondem 404)
|
|
137
143
|
getGroups(clientId) { return firstValueFrom(this.http.get(this.url `/${clientId}/groups`, { headers: this.headers })); }
|
|
144
|
+
/** Tipos (crachás) do realm; `assignable: false` no Administrador, que é só do grupo de administração (painel central). */
|
|
145
|
+
getGroupTypes(clientId) { return firstValueFrom(this.http.get(this.url `/${clientId}/group-types`, { headers: this.headers })); }
|
|
146
|
+
createGroup(clientId, body) { return firstValueFrom(this.http.post(this.url `/${clientId}/groups`, body, { headers: this.headers })); }
|
|
147
|
+
updateGroup(clientId, groupId, body) { return firstValueFrom(this.http.put(this.url `/${clientId}/groups/${groupId}`, body, { headers: this.headers })); }
|
|
148
|
+
deleteGroup(clientId, groupId) { return firstValueFrom(this.http.delete(this.url `/${clientId}/groups/${groupId}`, { headers: this.headers })); }
|
|
138
149
|
getGroupMembers(clientId, groupId) { return firstValueFrom(this.http.get(this.url `/${clientId}/groups/${groupId}/members`, { headers: this.headers })); }
|
|
139
150
|
addGroupMember(clientId, groupId, userId, role) { return firstValueFrom(this.http.post(this.url `/${clientId}/groups/${groupId}/members`, { user_id: userId, role }, { headers: this.headers })); }
|
|
140
151
|
updateGroupMemberRole(clientId, groupId, userId, role) { return firstValueFrom(this.http.put(this.url `/${clientId}/groups/${groupId}/members/${userId}`, { role }, { headers: this.headers })); }
|
|
@@ -159,9 +170,18 @@ class AutoServiceService {
|
|
|
159
170
|
getApps(clientId) { return firstValueFrom(this.http.get(this.url `/${clientId}/apps`, { headers: this.headers })); }
|
|
160
171
|
createApp(clientId, body) { return firstValueFrom(this.http.post(this.url `/${clientId}/apps`, body, { headers: this.headers })); }
|
|
161
172
|
updateApp(clientId, appId, body) { return firstValueFrom(this.http.put(this.url `/${clientId}/apps/${appId}`, body, { headers: this.headers })); }
|
|
173
|
+
/** Gera um segredo novo para a aplicação (o anterior para de valer na hora). A resposta traz o segredo uma única vez. */
|
|
174
|
+
rotateAppSecret(clientId, appId) { return firstValueFrom(this.http.post(this.url `/${clientId}/apps/${appId}/secret`, {}, { headers: this.headers })); }
|
|
162
175
|
deleteApp(clientId, appId) { return firstValueFrom(this.http.delete(this.url `/${clientId}/apps/${appId}`, { headers: this.headers })); }
|
|
163
176
|
getAppOperations(clientId, appId) { return firstValueFrom(this.http.get(this.url `/${clientId}/apps/${appId}/operations`, { headers: this.headers })); }
|
|
164
|
-
//
|
|
177
|
+
// Tokens de API da aplicação (x-api-token)
|
|
178
|
+
getAppTokens(clientId, appId) { return firstValueFrom(this.http.get(this.url `/${clientId}/apps/${appId}/tokens`, { headers: this.headers })); }
|
|
179
|
+
/** Cria o token (e o consumer dele, com as permissões escolhidas). A resposta traz o valor uma única vez. */
|
|
180
|
+
createAppToken(clientId, appId, body) { return firstValueFrom(this.http.post(this.url `/${clientId}/apps/${appId}/tokens`, body, { headers: this.headers })); }
|
|
181
|
+
/** Revoga: o token para de valer na hora e continua na lista como revogado. */
|
|
182
|
+
revokeAppToken(clientId, appId, tokenId) { return firstValueFrom(this.http.delete(this.url `/${clientId}/apps/${appId}/tokens/${tokenId}`, { headers: this.headers })); }
|
|
183
|
+
// Consumers (grupo ↔ aplicação), sem o do grupo de administração: o servidor não o lista e o :consumerId dele é 404
|
|
184
|
+
// (servidor antigo ainda devolve com `group_is_admin: true` e a aba Acessos descarta)
|
|
165
185
|
getAppConsumers(clientId, appId) { return firstValueFrom(this.http.get(this.url `/${clientId}/apps/${appId}/consumers`, { headers: this.headers })); }
|
|
166
186
|
createAppConsumer(clientId, appId, groupId) { return firstValueFrom(this.http.post(this.url `/${clientId}/apps/${appId}/consumers`, { group_id: groupId }, { headers: this.headers })); }
|
|
167
187
|
deleteAppConsumer(clientId, consumerId) { return firstValueFrom(this.http.delete(this.url `/${clientId}/apps/consumers/${consumerId}`, { headers: this.headers })); }
|
|
@@ -192,5 +212,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
192
212
|
* Generated bundle index. Do not edit.
|
|
193
213
|
*/
|
|
194
214
|
|
|
195
|
-
export { AUTO_SERVICE_APPS_TEMPLATE_ROUTER_ID, AUTO_SERVICE_AUDIT_TEMPLATE_ROUTER_ID, AUTO_SERVICE_CLIENT_TEMPLATE_ROUTER_ID, AUTO_SERVICE_CONFIG, AUTO_SERVICE_DOCUMENTS, AUTO_SERVICE_GROUPS_TEMPLATE_ROUTER_ID, AUTO_SERVICE_HOME_TEMPLATE_ROUTER_ID, AUTO_SERVICE_SCREENS, AutoServiceService, provideAutoService };
|
|
215
|
+
export { AUTO_SERVICE_APPS_TEMPLATE_ROUTER_ID, AUTO_SERVICE_AUDIT_TEMPLATE_ROUTER_ID, AUTO_SERVICE_CLIENT_TEMPLATE_ROUTER_ID, AUTO_SERVICE_CONFIG, AUTO_SERVICE_DOCUMENTS, AUTO_SERVICE_GROUPS_TEMPLATE_ROUTER_ID, AUTO_SERVICE_HOME_TEMPLATE_ROUTER_ID, AUTO_SERVICE_LOGIN_ATTEMPTS_TEMPLATE_ROUTER_ID, AUTO_SERVICE_SCREENS, AutoServiceService, provideAutoService };
|
|
196
216
|
//# sourceMappingURL=trismegisto-spine-auto-service.mjs.map
|