@memberjunction/ng-dashboards 5.47.0 → 5.49.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/dist/DataExplorer/data-explorer-dashboard.component.d.ts +15 -4
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +45 -35
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/DataExplorer/models/explorer-state.interface.d.ts +7 -0
- package/dist/DataExplorer/models/explorer-state.interface.d.ts.map +1 -1
- package/dist/DataExplorer/models/explorer-state.interface.js.map +1 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js +2 -2
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.d.ts +4 -0
- package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.js +39 -30
- package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.js +14 -12
- package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.js +6 -1
- package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
- package/dist/Lists/services/list-set-operations.service.d.ts +11 -0
- package/dist/Lists/services/list-set-operations.service.d.ts.map +1 -1
- package/dist/Lists/services/list-set-operations.service.js +56 -14
- package/dist/Lists/services/list-set-operations.service.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts +7 -1
- package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipelines.component.js +11 -4
- package/dist/PredictiveStudio/components/ps-pipelines.component.js.map +1 -1
- package/dist/Testing/components/testing-runs.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-runs.component.js +35 -25
- package/dist/Testing/components/testing-runs.component.js.map +1 -1
- package/dist/Testing/services/testing-instrumentation.service.d.ts +5 -0
- package/dist/Testing/services/testing-instrumentation.service.d.ts.map +1 -1
- package/dist/Testing/services/testing-instrumentation.service.js +8 -2
- package/dist/Testing/services/testing-instrumentation.service.js.map +1 -1
- package/dist/ThemeStudio/theme-agent-context.d.ts +64 -0
- package/dist/ThemeStudio/theme-agent-context.d.ts.map +1 -0
- package/dist/ThemeStudio/theme-agent-context.js +129 -0
- package/dist/ThemeStudio/theme-agent-context.js.map +1 -0
- package/dist/ThemeStudio/theme-manager-dashboard.component.d.ts +62 -0
- package/dist/ThemeStudio/theme-manager-dashboard.component.d.ts.map +1 -0
- package/dist/ThemeStudio/theme-manager-dashboard.component.js +464 -0
- package/dist/ThemeStudio/theme-manager-dashboard.component.js.map +1 -0
- package/dist/ThemeStudio/theme-manager-resource.component.d.ts +31 -0
- package/dist/ThemeStudio/theme-manager-resource.component.d.ts.map +1 -0
- package/dist/ThemeStudio/theme-manager-resource.component.js +97 -0
- package/dist/ThemeStudio/theme-manager-resource.component.js.map +1 -0
- package/dist/ThemeStudio/theme-studio-dashboard.component.d.ts +351 -0
- package/dist/ThemeStudio/theme-studio-dashboard.component.d.ts.map +1 -0
- package/dist/ThemeStudio/theme-studio-dashboard.component.js +2931 -0
- package/dist/ThemeStudio/theme-studio-dashboard.component.js.map +1 -0
- package/dist/ThemeStudio/theme-studio-resource.component.d.ts +28 -0
- package/dist/ThemeStudio/theme-studio-resource.component.d.ts.map +1 -0
- package/dist/ThemeStudio/theme-studio-resource.component.js +96 -0
- package/dist/ThemeStudio/theme-studio-resource.component.js.map +1 -0
- package/dist/ThemeStudio/theme-studio.constants.d.ts +79 -0
- package/dist/ThemeStudio/theme-studio.constants.d.ts.map +1 -0
- package/dist/ThemeStudio/theme-studio.constants.js +217 -0
- package/dist/ThemeStudio/theme-studio.constants.js.map +1 -0
- package/dist/core-dashboards.module.d.ts +56 -52
- package/dist/core-dashboards.module.d.ts.map +1 -1
- package/dist/core-dashboards.module.js +24 -1
- package/dist/core-dashboards.module.js.map +1 -1
- package/dist/public-api.d.ts +4 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +4 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +63 -60
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-agent-context.js","sourceRoot":"","sources":["../../src/ThemeStudio/theme-agent-context.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure, unit-testable helpers for the Theme Studio + Theme Manager agent context\n * and tools (context builders + tolerant id→name→contains resolver).\n *\n * 🚨 SAFETY BOUNDARY 🚨\n * The theming surfaces expose ONLY read / navigate / preview-state / user-scoped\n * tools to the agent: list + select themes, apply presets and seed edits to the\n * IN-MEMORY editor draft (nothing persists until the user saves), flip preview\n * mode/surface, apply a theme to the CURRENT USER's workspace, and star/unstar\n * (both per-user preferences). DELIBERATELY NOT exposed (org-wide or destructive\n * writes stay human-driven in the UI): SAVE a theme, RENAME, SET ORG DEFAULT,\n * DUPLICATE (creates records), and DELETE. Context exposes only theme display\n * names, statuses, seed values, and contrast pass/fail summaries — never the\n * Overrides / CustomCSS bodies.\n */\n\nimport { UUIDsEqual } from '@memberjunction/global';\nimport type { ContrastReport, ThemeSeeds } from '@memberjunction/theme-engine';\n\n/** Cap applied to every published name list. */\nexport const THEME_NAME_LIST_CAP = 25;\n\n/** Minimal theme shape the pure helpers operate on. */\nexport interface ThemeSummaryRow {\n ID: string;\n Name: string;\n Status: string;\n IsDefault: boolean;\n BuiltIn: boolean;\n}\n\n/** State snapshot the Theme Manager passes to its context builder. */\nexport interface ThemeManagerAgentState {\n Themes: ThemeSummaryRow[];\n AppliedThemeID: string | null;\n StarredThemeIDs: string[];\n}\n\n/** State snapshot the Theme Studio passes to its context builder. */\nexport interface ThemeStudioAgentState {\n Themes: ThemeSummaryRow[];\n CurrentThemeID: string | null;\n CurrentThemeName: string;\n IsBuiltInSelected: boolean;\n PreviewMode: 'light' | 'dark';\n PreviewSurface: string;\n EditorPanelOpen: boolean;\n Seeds: ThemeSeeds;\n /** Advanced-layer shape only — never the bodies. */\n OverrideTokenCount: number;\n HasCustomCss: boolean;\n Contrast: ContrastReport;\n}\n\n/** Bound a name list to the cap, reporting truncation via the returned tuple. */\nfunction boundNames(names: string[]): { names: string[]; truncated: boolean; total: number } {\n const filtered = names.filter((n) => !!n);\n return {\n names: filtered.slice(0, THEME_NAME_LIST_CAP),\n truncated: filtered.length > THEME_NAME_LIST_CAP,\n total: filtered.length,\n };\n}\n\n/** Bounded, secret-free context published by the Theme Manager via SetAgentContext. */\nexport function buildThemeManagerAgentContext(state: ThemeManagerAgentState): Record<string, unknown> {\n const applied = state.AppliedThemeID\n ? state.Themes.find((t) => UUIDsEqual(t.ID, state.AppliedThemeID as string)) ?? null\n : null;\n const bounded = boundNames(state.Themes.map((t) => t.Name));\n const context: Record<string, unknown> = {\n TotalThemeCount: state.Themes.length,\n ActiveThemeCount: state.Themes.filter((t) => t.Status === 'Active').length,\n DefaultThemeName: state.Themes.find((t) => t.IsDefault)?.Name ?? null,\n AppliedThemeID: state.AppliedThemeID,\n AppliedThemeName: applied?.Name ?? null,\n StarredThemeCount: state.StarredThemeIDs.length,\n VisibleThemeNames: bounded.names,\n };\n if (bounded.truncated) {\n context['VisibleThemeNamesTruncated'] = true;\n context['VisibleThemeNamesTotal'] = bounded.total;\n }\n return context;\n}\n\n/** Bounded, secret-free context published by the Theme Studio via SetAgentContext. */\nexport function buildThemeStudioAgentContext(state: ThemeStudioAgentState): Record<string, unknown> {\n const bounded = boundNames(state.Themes.map((t) => t.Name));\n const failingPairs = (checks: ContrastReport['light']) =>\n checks.filter((c) => !c.passes).map((c) => c.name).slice(0, THEME_NAME_LIST_CAP);\n const context: Record<string, unknown> = {\n CurrentThemeID: state.CurrentThemeID,\n CurrentThemeName: state.CurrentThemeName,\n IsUnsavedDraft: state.CurrentThemeID === null,\n IsBuiltInReadOnly: state.IsBuiltInSelected,\n PreviewMode: state.PreviewMode,\n PreviewSurface: state.PreviewSurface,\n EditorPanelOpen: state.EditorPanelOpen,\n Seeds: {\n Primary: state.Seeds.primary,\n Accent: state.Seeds.accent ?? null,\n Tertiary: state.Seeds.tertiary ?? null,\n NeutralChroma: state.Seeds.neutralChroma ?? null,\n Vibrancy: state.Seeds.vibrancy ?? null,\n Radius: state.Seeds.radius ?? null,\n Depth: state.Seeds.depth ?? null,\n FontFamily: state.Seeds.fontFamily ?? null,\n },\n VizPaletteOverridden: !!state.Seeds.vizPalette && state.Seeds.vizPalette.length > 0,\n OverrideTokenCount: state.OverrideTokenCount,\n HasCustomCss: state.HasCustomCss,\n ContrastPasses: state.Contrast.passes,\n ContrastFailingLight: failingPairs(state.Contrast.light),\n ContrastFailingDark: failingPairs(state.Contrast.dark),\n AvailableThemeNames: bounded.names,\n };\n if (bounded.truncated) {\n context['AvailableThemeNamesTruncated'] = true;\n context['AvailableThemeNamesTotal'] = bounded.total;\n }\n return context;\n}\n\n/**\n * Tolerant theme resolver: exact ID (case-insensitive GUID) → exact name →\n * partial name contains (all case-insensitive). Returns a structured failure\n * listing available names (bounded) on a miss.\n */\nexport function resolveThemeByIDOrName(\n themes: ThemeSummaryRow[],\n rawRef: unknown\n): { ok: true; value: ThemeSummaryRow } | { ok: false; error: string } {\n if (typeof rawRef !== 'string' || rawRef.trim().length === 0) {\n return { ok: false, error: `Provide the theme's ID or name. ${availableNames(themes)}` };\n }\n const ref = rawRef.trim();\n const byId = themes.find((t) => UUIDsEqual(t.ID, ref));\n if (byId) {\n return { ok: true, value: byId };\n }\n const byName = themes.find((t) => equalsIgnoreCase(t.Name, ref));\n if (byName) {\n return { ok: true, value: byName };\n }\n const contains = themes.filter((t) => t.Name.toLowerCase().includes(ref.toLowerCase()));\n if (contains.length === 1) {\n return { ok: true, value: contains[0] };\n }\n if (contains.length > 1) {\n return {\n ok: false,\n error: `'${ref}' matches multiple themes: ${contains\n .slice(0, THEME_NAME_LIST_CAP)\n .map((t) => t.Name)\n .join(', ')}. Be more specific.`,\n };\n }\n return { ok: false, error: `No theme matches '${ref}'. ${availableNames(themes)}` };\n}\n\nfunction availableNames(themes: ThemeSummaryRow[]): string {\n if (themes.length === 0) {\n return 'There are no saved themes yet.';\n }\n const names = themes.slice(0, THEME_NAME_LIST_CAP).map((t) => t.Name);\n const suffix = themes.length > THEME_NAME_LIST_CAP ? ', …' : '';\n return `Available themes: ${names.join(', ')}${suffix}`;\n}\n\nfunction equalsIgnoreCase(a: string, b: string): boolean {\n return a.trim().toLowerCase() === b.trim().toLowerCase();\n}\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Theme Manager dashboard (org-theming Phase 5) — the "Manage Themes"
|
|
3
|
+
* top-level tab of the Theme Studio application. A table of saved brand themes with
|
|
4
|
+
* govern actions: set default (applied live), duplicate, delete, and open/edit. The
|
|
5
|
+
* visual authoring happens in the sibling "Theme Studio" tab.
|
|
6
|
+
*
|
|
7
|
+
* 🚨 SAFETY BOUNDARY 🚨 (see theme-agent-context.ts for the shared statement)
|
|
8
|
+
* Agent tools here are read / user-scoped only: refresh, apply-to-me, star/unstar.
|
|
9
|
+
* Set-org-default, duplicate, delete, and edit stay human-driven in the UI —
|
|
10
|
+
* delete additionally sits behind MJConfirmService.ConfirmDelete.
|
|
11
|
+
* @module ThemeStudio
|
|
12
|
+
*/
|
|
13
|
+
import { ChangeDetectorRef, OnDestroy } from '@angular/core';
|
|
14
|
+
import { BaseDashboard, ThemeService } from '@memberjunction/ng-shared';
|
|
15
|
+
import { ResourceData } from '@memberjunction/core-entities';
|
|
16
|
+
import { MJConfirmService } from '@memberjunction/ng-ui-components';
|
|
17
|
+
import * as i0 from "@angular/core";
|
|
18
|
+
interface ThemeRow {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
isDefault: boolean;
|
|
22
|
+
status: string;
|
|
23
|
+
swatches: string[];
|
|
24
|
+
builtIn: boolean;
|
|
25
|
+
starred: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare class ThemeManagerDashboardComponent extends BaseDashboard implements OnDestroy {
|
|
28
|
+
private cdRef;
|
|
29
|
+
private themeService;
|
|
30
|
+
private confirmService;
|
|
31
|
+
themes: ThemeRow[];
|
|
32
|
+
private themesChangedSub?;
|
|
33
|
+
constructor(cdRef: ChangeDetectorRef, themeService: ThemeService, confirmService: MJConfirmService);
|
|
34
|
+
ngOnDestroy(): void;
|
|
35
|
+
GetResourceDisplayName(_data: ResourceData): Promise<string>;
|
|
36
|
+
/** Whether this theme is the brand currently applied to the session (case-insensitive
|
|
37
|
+
* GUID compare — RunView ids and entity ids can differ in case). */
|
|
38
|
+
isApplied(id: string): boolean;
|
|
39
|
+
protected initDashboard(): void;
|
|
40
|
+
protected loadData(): Promise<void>;
|
|
41
|
+
private swatchesFor;
|
|
42
|
+
/** Star / unstar a theme (adds it to the user's starred set for the user-menu modal). */
|
|
43
|
+
toggleStar(row: ThemeRow): Promise<void>;
|
|
44
|
+
/** Open the theme record (edit) — the shell handles this via the resource wrapper. */
|
|
45
|
+
edit(row: ThemeRow): void;
|
|
46
|
+
/** Apply this theme to the current user's workspace now and remember it as their choice. */
|
|
47
|
+
applyToMe(row: ThemeRow): Promise<void>;
|
|
48
|
+
/** Make this the single org-wide default and apply it to the running app immediately. */
|
|
49
|
+
setDefault(row: ThemeRow): Promise<void>;
|
|
50
|
+
duplicate(row: ThemeRow): Promise<void>;
|
|
51
|
+
/** Delete a theme after an explicit confirmation (MJConfirmService owns the dialog). */
|
|
52
|
+
remove(row: ThemeRow): Promise<void>;
|
|
53
|
+
private uniqueName;
|
|
54
|
+
private notify;
|
|
55
|
+
private summaryRows;
|
|
56
|
+
private publishAgentContext;
|
|
57
|
+
private registerAgentTools;
|
|
58
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ThemeManagerDashboardComponent, never>;
|
|
59
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ThemeManagerDashboardComponent, "mj-theme-manager-dashboard", never, {}, {}, never, never, false, never>;
|
|
60
|
+
}
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=theme-manager-dashboard.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-manager-dashboard.component.d.ts","sourceRoot":"","sources":["../../src/ThemeStudio/theme-manager-dashboard.component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,iBAAiB,EAAa,SAAS,EAAE,MAAM,eAAe,CAAC;AAIxE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAiB,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;;AAKpE,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAOa,8BAA+B,SAAQ,aAAc,YAAW,SAAS;IAKlF,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,cAAc;IANjB,MAAM,EAAE,QAAQ,EAAE,CAAM;IAC/B,OAAO,CAAC,gBAAgB,CAAC,CAAe;gBAG9B,KAAK,EAAE,iBAAiB,EACxB,YAAY,EAAE,YAAY,EAC1B,cAAc,EAAE,gBAAgB;IAKjC,WAAW,IAAI,IAAI;IAKtB,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAIlE;yEACqE;IAC9D,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAKrC,SAAS,CAAC,aAAa,IAAI,IAAI;cAQf,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAwBzC,OAAO,CAAC,WAAW;IASnB,yFAAyF;IAC5E,UAAU,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrD,sFAAsF;IAC/E,IAAI,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI;IAQhC,4FAA4F;IAC/E,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBpD,yFAAyF;IAC5E,UAAU,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA2CxC,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBpD,wFAAwF;IAC3E,MAAM,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BjD,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,kBAAkB;yCA5Of,8BAA8B;2CAA9B,8BAA8B;CAyS1C"}
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* @fileoverview Theme Manager dashboard (org-theming Phase 5) — the "Manage Themes"
|
|
9
|
+
* top-level tab of the Theme Studio application. A table of saved brand themes with
|
|
10
|
+
* govern actions: set default (applied live), duplicate, delete, and open/edit. The
|
|
11
|
+
* visual authoring happens in the sibling "Theme Studio" tab.
|
|
12
|
+
*
|
|
13
|
+
* 🚨 SAFETY BOUNDARY 🚨 (see theme-agent-context.ts for the shared statement)
|
|
14
|
+
* Agent tools here are read / user-scoped only: refresh, apply-to-me, star/unstar.
|
|
15
|
+
* Set-org-default, duplicate, delete, and edit stay human-driven in the UI —
|
|
16
|
+
* delete additionally sits behind MJConfirmService.ConfirmDelete.
|
|
17
|
+
* @module ThemeStudio
|
|
18
|
+
*/
|
|
19
|
+
import { Component } from '@angular/core';
|
|
20
|
+
import { CompositeKey, RunView } from '@memberjunction/core';
|
|
21
|
+
import { RegisterClass, UUIDsEqual } from '@memberjunction/global';
|
|
22
|
+
import { BaseDashboard } from '@memberjunction/ng-shared';
|
|
23
|
+
import { MJNotificationService } from '@memberjunction/ng-notifications';
|
|
24
|
+
import { MJ_DEFAULT_SEEDS } from '@memberjunction/theme-engine';
|
|
25
|
+
import { isBuiltInTheme } from './theme-studio.constants';
|
|
26
|
+
import { buildThemeManagerAgentContext, resolveThemeByIDOrName } from './theme-agent-context';
|
|
27
|
+
import * as i0 from "@angular/core";
|
|
28
|
+
import * as i1 from "@memberjunction/ng-shared";
|
|
29
|
+
import * as i2 from "@memberjunction/ng-ui-components";
|
|
30
|
+
const _forTrack0 = ($index, $item) => $item.id;
|
|
31
|
+
function ThemeManagerDashboardComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
32
|
+
i0.ɵɵelement(0, "mj-empty-state", 1);
|
|
33
|
+
} }
|
|
34
|
+
function ThemeManagerDashboardComponent_Conditional_4_For_13_For_6_Template(rf, ctx) { if (rf & 1) {
|
|
35
|
+
i0.ɵɵelement(0, "i");
|
|
36
|
+
} if (rf & 2) {
|
|
37
|
+
const c_r4 = ctx.$implicit;
|
|
38
|
+
i0.ɵɵstyleProp("background", c_r4);
|
|
39
|
+
} }
|
|
40
|
+
function ThemeManagerDashboardComponent_Conditional_4_For_13_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
41
|
+
i0.ɵɵelementStart(0, "span", 8);
|
|
42
|
+
i0.ɵɵelement(1, "i", 15);
|
|
43
|
+
i0.ɵɵtext(2, " Built-in");
|
|
44
|
+
i0.ɵɵelementEnd();
|
|
45
|
+
} }
|
|
46
|
+
function ThemeManagerDashboardComponent_Conditional_4_For_13_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
47
|
+
i0.ɵɵelementStart(0, "span", 10);
|
|
48
|
+
i0.ɵɵtext(1, "DEFAULT");
|
|
49
|
+
i0.ɵɵelementEnd();
|
|
50
|
+
} }
|
|
51
|
+
function ThemeManagerDashboardComponent_Conditional_4_For_13_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
52
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
53
|
+
i0.ɵɵelementStart(0, "button", 16);
|
|
54
|
+
i0.ɵɵlistener("click", function ThemeManagerDashboardComponent_Conditional_4_For_13_Conditional_15_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r5); const t_r2 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.setDefault(t_r2)); });
|
|
55
|
+
i0.ɵɵtext(1, "Set as org default");
|
|
56
|
+
i0.ɵɵelementEnd();
|
|
57
|
+
} }
|
|
58
|
+
function ThemeManagerDashboardComponent_Conditional_4_For_13_Conditional_21_Template(rf, ctx) { if (rf & 1) {
|
|
59
|
+
const _r6 = i0.ɵɵgetCurrentView();
|
|
60
|
+
i0.ɵɵelementStart(0, "button", 14);
|
|
61
|
+
i0.ɵɵlistener("click", function ThemeManagerDashboardComponent_Conditional_4_For_13_Conditional_21_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r6); const t_r2 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.edit(t_r2)); });
|
|
62
|
+
i0.ɵɵtext(1, "Edit");
|
|
63
|
+
i0.ɵɵelementEnd();
|
|
64
|
+
i0.ɵɵelementStart(2, "button", 17);
|
|
65
|
+
i0.ɵɵlistener("click", function ThemeManagerDashboardComponent_Conditional_4_For_13_Conditional_21_Template_button_click_2_listener() { i0.ɵɵrestoreView(_r6); const t_r2 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.remove(t_r2)); });
|
|
66
|
+
i0.ɵɵtext(3, "Delete");
|
|
67
|
+
i0.ɵɵelementEnd();
|
|
68
|
+
} }
|
|
69
|
+
function ThemeManagerDashboardComponent_Conditional_4_For_13_Template(rf, ctx) { if (rf & 1) {
|
|
70
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
71
|
+
i0.ɵɵelementStart(0, "tr")(1, "td", 4)(2, "button", 5);
|
|
72
|
+
i0.ɵɵlistener("click", function ThemeManagerDashboardComponent_Conditional_4_For_13_Template_button_click_2_listener() { const t_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.toggleStar(t_r2)); });
|
|
73
|
+
i0.ɵɵelement(3, "i");
|
|
74
|
+
i0.ɵɵelementEnd();
|
|
75
|
+
i0.ɵɵelementStart(4, "span", 6);
|
|
76
|
+
i0.ɵɵrepeaterCreate(5, ThemeManagerDashboardComponent_Conditional_4_For_13_For_6_Template, 1, 2, "i", 7, i0.ɵɵrepeaterTrackByIndex);
|
|
77
|
+
i0.ɵɵelementEnd();
|
|
78
|
+
i0.ɵɵelementStart(7, "b");
|
|
79
|
+
i0.ɵɵtext(8);
|
|
80
|
+
i0.ɵɵelementEnd();
|
|
81
|
+
i0.ɵɵconditionalCreate(9, ThemeManagerDashboardComponent_Conditional_4_For_13_Conditional_9_Template, 3, 0, "span", 8);
|
|
82
|
+
i0.ɵɵelementEnd();
|
|
83
|
+
i0.ɵɵelementStart(10, "td")(11, "span", 9);
|
|
84
|
+
i0.ɵɵtext(12);
|
|
85
|
+
i0.ɵɵelementEnd()();
|
|
86
|
+
i0.ɵɵelementStart(13, "td");
|
|
87
|
+
i0.ɵɵconditionalCreate(14, ThemeManagerDashboardComponent_Conditional_4_For_13_Conditional_14_Template, 2, 0, "span", 10)(15, ThemeManagerDashboardComponent_Conditional_4_For_13_Conditional_15_Template, 2, 0, "button", 11);
|
|
88
|
+
i0.ɵɵelementEnd();
|
|
89
|
+
i0.ɵɵelementStart(16, "td", 12)(17, "button", 13);
|
|
90
|
+
i0.ɵɵlistener("click", function ThemeManagerDashboardComponent_Conditional_4_For_13_Template_button_click_17_listener() { const t_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.applyToMe(t_r2)); });
|
|
91
|
+
i0.ɵɵtext(18);
|
|
92
|
+
i0.ɵɵelementEnd();
|
|
93
|
+
i0.ɵɵelementStart(19, "button", 14);
|
|
94
|
+
i0.ɵɵlistener("click", function ThemeManagerDashboardComponent_Conditional_4_For_13_Template_button_click_19_listener() { const t_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.duplicate(t_r2)); });
|
|
95
|
+
i0.ɵɵtext(20, "Duplicate");
|
|
96
|
+
i0.ɵɵelementEnd();
|
|
97
|
+
i0.ɵɵconditionalCreate(21, ThemeManagerDashboardComponent_Conditional_4_For_13_Conditional_21_Template, 4, 0);
|
|
98
|
+
i0.ɵɵelementEnd()();
|
|
99
|
+
} if (rf & 2) {
|
|
100
|
+
const t_r2 = ctx.$implicit;
|
|
101
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
102
|
+
i0.ɵɵadvance(2);
|
|
103
|
+
i0.ɵɵclassProp("on", t_r2.starred);
|
|
104
|
+
i0.ɵɵproperty("title", t_r2.starred ? "Unstar" : "Star (adds to your user menu)");
|
|
105
|
+
i0.ɵɵadvance();
|
|
106
|
+
i0.ɵɵclassMap(t_r2.starred ? "fa-solid fa-star" : "fa-regular fa-star");
|
|
107
|
+
i0.ɵɵadvance(2);
|
|
108
|
+
i0.ɵɵrepeater(t_r2.swatches);
|
|
109
|
+
i0.ɵɵadvance(3);
|
|
110
|
+
i0.ɵɵtextInterpolate(t_r2.name);
|
|
111
|
+
i0.ɵɵadvance();
|
|
112
|
+
i0.ɵɵconditional(t_r2.builtIn ? 9 : -1);
|
|
113
|
+
i0.ɵɵadvance(2);
|
|
114
|
+
i0.ɵɵclassProp("inactive", t_r2.status !== "Active");
|
|
115
|
+
i0.ɵɵadvance();
|
|
116
|
+
i0.ɵɵtextInterpolate(t_r2.status);
|
|
117
|
+
i0.ɵɵadvance(2);
|
|
118
|
+
i0.ɵɵconditional(t_r2.isDefault ? 14 : 15);
|
|
119
|
+
i0.ɵɵadvance(3);
|
|
120
|
+
i0.ɵɵproperty("disabled", ctx_r2.isApplied(t_r2.id));
|
|
121
|
+
i0.ɵɵadvance();
|
|
122
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r2.isApplied(t_r2.id) ? "Applied" : "Apply", " ");
|
|
123
|
+
i0.ɵɵadvance(3);
|
|
124
|
+
i0.ɵɵconditional(!t_r2.builtIn ? 21 : -1);
|
|
125
|
+
} }
|
|
126
|
+
function ThemeManagerDashboardComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
127
|
+
i0.ɵɵelementStart(0, "table", 2)(1, "thead")(2, "tr")(3, "th");
|
|
128
|
+
i0.ɵɵtext(4, "Theme");
|
|
129
|
+
i0.ɵɵelementEnd();
|
|
130
|
+
i0.ɵɵelementStart(5, "th");
|
|
131
|
+
i0.ɵɵtext(6, "Status");
|
|
132
|
+
i0.ɵɵelementEnd();
|
|
133
|
+
i0.ɵɵelementStart(7, "th");
|
|
134
|
+
i0.ɵɵtext(8, "Default");
|
|
135
|
+
i0.ɵɵelementEnd();
|
|
136
|
+
i0.ɵɵelementStart(9, "th", 3);
|
|
137
|
+
i0.ɵɵtext(10, "Actions");
|
|
138
|
+
i0.ɵɵelementEnd()()();
|
|
139
|
+
i0.ɵɵelementStart(11, "tbody");
|
|
140
|
+
i0.ɵɵrepeaterCreate(12, ThemeManagerDashboardComponent_Conditional_4_For_13_Template, 22, 14, "tr", null, _forTrack0);
|
|
141
|
+
i0.ɵɵelementEnd()();
|
|
142
|
+
} if (rf & 2) {
|
|
143
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
144
|
+
i0.ɵɵadvance(12);
|
|
145
|
+
i0.ɵɵrepeater(ctx_r2.themes);
|
|
146
|
+
} }
|
|
147
|
+
let ThemeManagerDashboardComponent = class ThemeManagerDashboardComponent extends BaseDashboard {
|
|
148
|
+
cdRef;
|
|
149
|
+
themeService;
|
|
150
|
+
confirmService;
|
|
151
|
+
themes = [];
|
|
152
|
+
themesChangedSub;
|
|
153
|
+
constructor(cdRef, themeService, confirmService) {
|
|
154
|
+
super();
|
|
155
|
+
this.cdRef = cdRef;
|
|
156
|
+
this.themeService = themeService;
|
|
157
|
+
this.confirmService = confirmService;
|
|
158
|
+
}
|
|
159
|
+
ngOnDestroy() {
|
|
160
|
+
this.themesChangedSub?.unsubscribe();
|
|
161
|
+
super.ngOnDestroy();
|
|
162
|
+
}
|
|
163
|
+
async GetResourceDisplayName(_data) {
|
|
164
|
+
return 'Manage Themes';
|
|
165
|
+
}
|
|
166
|
+
/** Whether this theme is the brand currently applied to the session (case-insensitive
|
|
167
|
+
* GUID compare — RunView ids and entity ids can differ in case). */
|
|
168
|
+
isApplied(id) {
|
|
169
|
+
const active = this.themeService.BrandOverlayId;
|
|
170
|
+
return !!active && UUIDsEqual(id, active);
|
|
171
|
+
}
|
|
172
|
+
initDashboard() {
|
|
173
|
+
// Reload when a theme is created/renamed/etc. in the Theme Studio tab (or elsewhere).
|
|
174
|
+
this.themesChangedSub = this.themeService.ThemesChanged$.subscribe(() => {
|
|
175
|
+
this.loadData();
|
|
176
|
+
});
|
|
177
|
+
this.registerAgentTools();
|
|
178
|
+
}
|
|
179
|
+
async loadData() {
|
|
180
|
+
try {
|
|
181
|
+
const rv = new RunView();
|
|
182
|
+
const result = await rv.RunView({
|
|
183
|
+
EntityName: 'MJ: Themes',
|
|
184
|
+
OrderBy: 'Name',
|
|
185
|
+
ResultType: 'entity_object',
|
|
186
|
+
});
|
|
187
|
+
this.themes = (result.Success ? result.Results : []).map((t) => ({
|
|
188
|
+
id: t.ID,
|
|
189
|
+
name: t.Name,
|
|
190
|
+
isDefault: t.IsDefault,
|
|
191
|
+
status: t.Status,
|
|
192
|
+
swatches: this.swatchesFor(t.Seeds),
|
|
193
|
+
builtIn: isBuiltInTheme(t.ID),
|
|
194
|
+
starred: this.themeService.IsStarred(t.ID),
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
this.themes = [];
|
|
199
|
+
}
|
|
200
|
+
this.publishAgentContext();
|
|
201
|
+
this.cdRef.detectChanges();
|
|
202
|
+
}
|
|
203
|
+
swatchesFor(seedsJson) {
|
|
204
|
+
try {
|
|
205
|
+
const s = JSON.parse(seedsJson);
|
|
206
|
+
return [s.primary, s.accent ?? s.primary, s.tertiary ?? s.accent ?? s.primary];
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return ['#0076b6', '#38a9d9', '#8b5cf6'];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/** Star / unstar a theme (adds it to the user's starred set for the user-menu modal). */
|
|
213
|
+
async toggleStar(row) {
|
|
214
|
+
row.starred = await this.themeService.ToggleStar(row.id);
|
|
215
|
+
this.publishAgentContext();
|
|
216
|
+
this.cdRef.detectChanges();
|
|
217
|
+
}
|
|
218
|
+
/** Open the theme record (edit) — the shell handles this via the resource wrapper. */
|
|
219
|
+
edit(row) {
|
|
220
|
+
if (row.builtIn) {
|
|
221
|
+
this.notify('The built-in theme is read-only — duplicate it to customize.', 'info');
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
this.OpenEntityRecord.emit({ EntityName: 'MJ: Themes', RecordPKey: CompositeKey.FromID(row.id) });
|
|
225
|
+
}
|
|
226
|
+
/** Apply this theme to the current user's workspace now and remember it as their choice. */
|
|
227
|
+
async applyToMe(row) {
|
|
228
|
+
try {
|
|
229
|
+
const md = this.ProviderToUse;
|
|
230
|
+
const entity = await md.GetEntityObject('MJ: Themes');
|
|
231
|
+
if (!(await entity.Load(row.id)))
|
|
232
|
+
return;
|
|
233
|
+
this.themeService.RegisterBrandTheme({ id: entity.ID, name: entity.Name, seeds: entity.Seeds, overrides: entity.Overrides, customCss: entity.CustomCSS });
|
|
234
|
+
await this.themeService.ApplyBrandOverlay(entity.ID);
|
|
235
|
+
await this.themeService.SetSelectedBrandTheme(entity.ID);
|
|
236
|
+
this.publishAgentContext();
|
|
237
|
+
this.notify(`Applied "${entity.Name}" to your workspace.`);
|
|
238
|
+
}
|
|
239
|
+
catch (e) {
|
|
240
|
+
this.notify(`Could not apply: ${e instanceof Error ? e.message : String(e)}`, 'error');
|
|
241
|
+
}
|
|
242
|
+
finally {
|
|
243
|
+
this.cdRef.detectChanges();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/** Make this the single org-wide default and apply it to the running app immediately. */
|
|
247
|
+
async setDefault(row) {
|
|
248
|
+
try {
|
|
249
|
+
const md = this.ProviderToUse;
|
|
250
|
+
const rv = new RunView();
|
|
251
|
+
const others = await rv.RunView({
|
|
252
|
+
EntityName: 'MJ: Themes',
|
|
253
|
+
ExtraFilter: `IsDefault = 1 AND ID <> '${row.id}'`,
|
|
254
|
+
ResultType: 'entity_object',
|
|
255
|
+
});
|
|
256
|
+
if (others.Success) {
|
|
257
|
+
for (const other of others.Results) {
|
|
258
|
+
other.IsDefault = false;
|
|
259
|
+
if (!(await other.Save())) {
|
|
260
|
+
this.notify(`Could not clear the previous default "${other.Name}": ${other.LatestResult?.CompleteMessage ?? 'unknown error'}`, 'error');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const entity = await md.GetEntityObject('MJ: Themes');
|
|
266
|
+
if (!(await entity.Load(row.id)))
|
|
267
|
+
return;
|
|
268
|
+
entity.IsDefault = true;
|
|
269
|
+
if (!(await entity.Save())) {
|
|
270
|
+
this.notify(`Could not set default: ${entity.LatestResult?.CompleteMessage ?? 'unknown error'}`, 'error');
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
let seeds;
|
|
274
|
+
try {
|
|
275
|
+
seeds = { ...MJ_DEFAULT_SEEDS, ...JSON.parse(entity.Seeds) };
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
seeds = { ...MJ_DEFAULT_SEEDS };
|
|
279
|
+
}
|
|
280
|
+
this.themeService.RegisterBrandTheme({ id: entity.ID, name: entity.Name, seeds, overrides: entity.Overrides, customCss: entity.CustomCSS });
|
|
281
|
+
await this.themeService.ApplyBrandOverlay(entity.ID);
|
|
282
|
+
await this.loadData();
|
|
283
|
+
this.themeService.NotifyThemesChanged();
|
|
284
|
+
this.notify(`"${entity.Name}" is now the default and is applied.`);
|
|
285
|
+
}
|
|
286
|
+
catch (e) {
|
|
287
|
+
this.notify(`Could not set default: ${e instanceof Error ? e.message : String(e)}`, 'error');
|
|
288
|
+
}
|
|
289
|
+
finally {
|
|
290
|
+
this.cdRef.detectChanges();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async duplicate(row) {
|
|
294
|
+
try {
|
|
295
|
+
const md = this.ProviderToUse;
|
|
296
|
+
const src = await md.GetEntityObject('MJ: Themes');
|
|
297
|
+
if (!(await src.Load(row.id)))
|
|
298
|
+
return;
|
|
299
|
+
const copy = await md.GetEntityObject('MJ: Themes');
|
|
300
|
+
copy.NewRecord();
|
|
301
|
+
copy.Name = this.uniqueName(`${src.Name} Copy`);
|
|
302
|
+
copy.Description = src.Description;
|
|
303
|
+
copy.Seeds = src.Seeds;
|
|
304
|
+
copy.Status = 'Active';
|
|
305
|
+
if (await copy.Save()) {
|
|
306
|
+
await this.loadData();
|
|
307
|
+
this.themeService.NotifyThemesChanged();
|
|
308
|
+
this.notify(`Duplicated as "${copy.Name}".`);
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
this.notify(`Duplicate failed: ${copy.LatestResult?.CompleteMessage ?? 'unknown error'}`, 'error');
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch (e) {
|
|
315
|
+
this.notify(`Duplicate failed: ${e instanceof Error ? e.message : String(e)}`, 'error');
|
|
316
|
+
}
|
|
317
|
+
finally {
|
|
318
|
+
this.cdRef.detectChanges();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/** Delete a theme after an explicit confirmation (MJConfirmService owns the dialog). */
|
|
322
|
+
async remove(row) {
|
|
323
|
+
if (row.builtIn) {
|
|
324
|
+
this.notify('The built-in theme cannot be deleted.', 'info');
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const confirmed = await this.confirmService.ConfirmDelete({
|
|
328
|
+
title: 'Delete theme',
|
|
329
|
+
message: `Delete "${row.name}"? This can't be undone.`,
|
|
330
|
+
});
|
|
331
|
+
if (!confirmed) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
const md = this.ProviderToUse;
|
|
336
|
+
const entity = await md.GetEntityObject('MJ: Themes');
|
|
337
|
+
if (!(await entity.Load(row.id)))
|
|
338
|
+
return;
|
|
339
|
+
if (await entity.Delete()) {
|
|
340
|
+
await this.loadData();
|
|
341
|
+
this.themeService.NotifyThemesChanged();
|
|
342
|
+
this.notify(`Deleted "${row.name}".`);
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
this.notify(`Delete failed: ${entity.LatestResult?.CompleteMessage ?? 'unknown error'}`, 'error');
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
catch (e) {
|
|
349
|
+
this.notify(`Delete failed: ${e instanceof Error ? e.message : String(e)}`, 'error');
|
|
350
|
+
}
|
|
351
|
+
finally {
|
|
352
|
+
this.cdRef.detectChanges();
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
uniqueName(base) {
|
|
356
|
+
const taken = new Set(this.themes.map((t) => t.name.trim().toLowerCase()));
|
|
357
|
+
if (!taken.has(base.trim().toLowerCase()))
|
|
358
|
+
return base;
|
|
359
|
+
for (let i = 2; i < 1000; i++) {
|
|
360
|
+
const candidate = `${base} ${i}`;
|
|
361
|
+
if (!taken.has(candidate.toLowerCase()))
|
|
362
|
+
return candidate;
|
|
363
|
+
}
|
|
364
|
+
return `${base} ${Date.now()}`;
|
|
365
|
+
}
|
|
366
|
+
notify(message, style = 'success') {
|
|
367
|
+
MJNotificationService.Instance.CreateSimpleNotification(message, style, 2500);
|
|
368
|
+
}
|
|
369
|
+
// ---------------------------------------------------------------
|
|
370
|
+
// Agent context + tools (read / user-scoped only — see SAFETY BOUNDARY)
|
|
371
|
+
// ---------------------------------------------------------------
|
|
372
|
+
summaryRows() {
|
|
373
|
+
return this.themes.map((t) => ({ ID: t.id, Name: t.name, Status: t.status, IsDefault: t.isDefault, BuiltIn: t.builtIn }));
|
|
374
|
+
}
|
|
375
|
+
publishAgentContext() {
|
|
376
|
+
this.navigationService.SetAgentContext(this, buildThemeManagerAgentContext({
|
|
377
|
+
Themes: this.summaryRows(),
|
|
378
|
+
AppliedThemeID: this.themeService.BrandOverlayId,
|
|
379
|
+
StarredThemeIDs: this.themeService.GetStarredThemeIds(),
|
|
380
|
+
}));
|
|
381
|
+
}
|
|
382
|
+
registerAgentTools() {
|
|
383
|
+
this.navigationService.SetAgentClientTools(this, [
|
|
384
|
+
{
|
|
385
|
+
Name: 'RefreshThemes',
|
|
386
|
+
Description: 'Reload the saved brand themes list from the server.',
|
|
387
|
+
ParameterSchema: { type: 'object', properties: {} },
|
|
388
|
+
Handler: async () => {
|
|
389
|
+
await this.loadData();
|
|
390
|
+
return { Success: true, Data: { TotalThemeCount: this.themes.length } };
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
Name: 'ApplyTheme',
|
|
395
|
+
Description: "Apply a saved brand theme to the CURRENT USER's workspace (a per-user preference — not the org default), referenced by ID or name (partial, case-insensitive match accepted).",
|
|
396
|
+
ParameterSchema: {
|
|
397
|
+
type: 'object',
|
|
398
|
+
properties: { theme: { type: 'string', description: 'The theme ID or name.' } },
|
|
399
|
+
required: ['theme'],
|
|
400
|
+
},
|
|
401
|
+
Handler: async (params) => {
|
|
402
|
+
const resolved = resolveThemeByIDOrName(this.summaryRows(), params['theme']);
|
|
403
|
+
if (!resolved.ok) {
|
|
404
|
+
return { Success: false, ErrorMessage: resolved.error };
|
|
405
|
+
}
|
|
406
|
+
const row = this.themes.find((t) => UUIDsEqual(t.id, resolved.value.ID));
|
|
407
|
+
if (!row) {
|
|
408
|
+
return { Success: false, ErrorMessage: 'Theme list changed — refresh and retry.' };
|
|
409
|
+
}
|
|
410
|
+
await this.applyToMe(row);
|
|
411
|
+
return { Success: true, Data: { AppliedThemeName: resolved.value.Name } };
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
Name: 'StarTheme',
|
|
416
|
+
Description: 'Star or unstar a theme for the current user (starred themes appear in the user menu), referenced by ID or name. Toggles when no explicit starred value is given.',
|
|
417
|
+
ParameterSchema: {
|
|
418
|
+
type: 'object',
|
|
419
|
+
properties: {
|
|
420
|
+
theme: { type: 'string', description: 'The theme ID or name.' },
|
|
421
|
+
starred: { type: 'boolean', description: 'true to star, false to unstar; omit to toggle.' },
|
|
422
|
+
},
|
|
423
|
+
required: ['theme'],
|
|
424
|
+
},
|
|
425
|
+
Handler: async (params) => {
|
|
426
|
+
const resolved = resolveThemeByIDOrName(this.summaryRows(), params['theme']);
|
|
427
|
+
if (!resolved.ok) {
|
|
428
|
+
return { Success: false, ErrorMessage: resolved.error };
|
|
429
|
+
}
|
|
430
|
+
const row = this.themes.find((t) => UUIDsEqual(t.id, resolved.value.ID));
|
|
431
|
+
if (!row) {
|
|
432
|
+
return { Success: false, ErrorMessage: 'Theme list changed — refresh and retry.' };
|
|
433
|
+
}
|
|
434
|
+
const wantStarred = typeof params['starred'] === 'boolean' ? params['starred'] : !row.starred;
|
|
435
|
+
if (wantStarred !== row.starred) {
|
|
436
|
+
await this.toggleStar(row);
|
|
437
|
+
}
|
|
438
|
+
return { Success: true, Data: { ThemeName: row.name, Starred: row.starred } };
|
|
439
|
+
},
|
|
440
|
+
},
|
|
441
|
+
]);
|
|
442
|
+
}
|
|
443
|
+
static ɵfac = function ThemeManagerDashboardComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ThemeManagerDashboardComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1.ThemeService), i0.ɵɵdirectiveInject(i2.MJConfirmService)); };
|
|
444
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ThemeManagerDashboardComponent, selectors: [["mj-theme-manager-dashboard"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 1, consts: [["Title", "Manage Themes", "Icon", "fa-solid fa-swatchbook", "Subtitle", "Your organization's saved brand themes"], ["Icon", "fa-solid fa-swatchbook", "Title", "No themes yet", "Message", "Create one in the Theme Studio tab."], [1, "tm-table"], [1, "r"], [1, "name-cell"], ["type", "button", 1, "star-btn", 3, "click", "title"], [1, "sw"], [3, "background"], [1, "builtin-badge"], [1, "status-pill"], [1, "badge"], ["type", "button", "title", "Make this the organization-wide default", 1, "link-btn"], [1, "r", "actions"], ["mjButton", "", "variant", "primary", "size", "sm", "title", "Apply this theme to your workspace now", 3, "click", "disabled"], ["mjButton", "", "variant", "outline", "size", "sm", 3, "click"], [1, "fa-solid", "fa-lock"], ["type", "button", "title", "Make this the organization-wide default", 1, "link-btn", 3, "click"], ["mjButton", "", "variant", "danger", "size", "sm", 3, "click"]], template: function ThemeManagerDashboardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
445
|
+
i0.ɵɵelementStart(0, "mj-page-layout");
|
|
446
|
+
i0.ɵɵelement(1, "mj-page-header", 0);
|
|
447
|
+
i0.ɵɵelementStart(2, "mj-page-body");
|
|
448
|
+
i0.ɵɵconditionalCreate(3, ThemeManagerDashboardComponent_Conditional_3_Template, 1, 0, "mj-empty-state", 1)(4, ThemeManagerDashboardComponent_Conditional_4_Template, 14, 0, "table", 2);
|
|
449
|
+
i0.ɵɵelementEnd()();
|
|
450
|
+
} if (rf & 2) {
|
|
451
|
+
i0.ɵɵadvance(3);
|
|
452
|
+
i0.ɵɵconditional(!ctx.themes.length ? 3 : 4);
|
|
453
|
+
} }, dependencies: [i2.MJButtonDirective, i2.MJPageHeaderComponent, i2.MJPageLayoutComponent, i2.MJPageBodyComponent, i2.MJEmptyStateComponent], styles: ["\n\n\n[_nghost-%COMP%] { display: block; width: 100%; height: 100%; }\n\n.tm-table[_ngcontent-%COMP%] { width: 100%; max-width: 1100px; margin: 0 auto; border-collapse: collapse; background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default); border-radius: 12px; overflow: hidden; box-shadow: var(--mj-shadow-sm); }\n.tm-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] { text-align: left; font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--mj-text-muted); background: var(--mj-bg-surface-card); padding: 11px 16px; border-bottom: 1px solid var(--mj-border-default); }\n.tm-table[_ngcontent-%COMP%] th.r[_ngcontent-%COMP%], .tm-table[_ngcontent-%COMP%] td.r[_ngcontent-%COMP%] { text-align: right; }\n.tm-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] { padding: 11px 16px; border-bottom: 1px solid var(--mj-border-subtle); font-size: 13px; color: var(--mj-text-secondary); vertical-align: middle; }\n.tm-table[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:last-child td[_ngcontent-%COMP%] { border-bottom: 0; }\n.name-cell[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 10px; }\n.star-btn[_ngcontent-%COMP%] { border: 0; background: transparent; color: var(--mj-text-muted); cursor: pointer; font-size: 15px; padding: 2px; line-height: 1; }\n.star-btn[_ngcontent-%COMP%]:hover { color: var(--mj-status-warning); }\n.star-btn.on[_ngcontent-%COMP%] { color: var(--mj-status-warning); }\n.name-cell[_ngcontent-%COMP%] b[_ngcontent-%COMP%] { color: var(--mj-text-primary); font-weight: 600; }\n.sw[_ngcontent-%COMP%] { display: flex; flex-shrink: 0; }\n.sw[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { width: 12px; height: 20px; display: block; margin-left: -4px; border-radius: 3px; border: 1.5px solid var(--mj-bg-surface); }\n.sw[_ngcontent-%COMP%] i[_ngcontent-%COMP%]:first-child { margin-left: 0; }\n.badge[_ngcontent-%COMP%] { font-size: 9.5px; font-weight: 700; letter-spacing: .04em; color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 14%, transparent); padding: 2px 6px; border-radius: 20px; }\n.status-pill[_ngcontent-%COMP%] { font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 20px; background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.status-pill.inactive[_ngcontent-%COMP%] { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n.builtin-badge[_ngcontent-%COMP%] { display: inline-flex; align-items: center; gap: 4px; font-size: 10px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--mj-text-secondary); background: var(--mj-bg-surface-sunken); border: 1px solid var(--mj-border-default); padding: 2px 7px; border-radius: 20px; }\n.link-btn[_ngcontent-%COMP%] { border: 0; background: transparent; color: var(--mj-text-link); font: inherit; font-size: 12.5px; cursor: pointer; padding: 0; }\n.actions[_ngcontent-%COMP%] { display: flex; align-items: center; justify-content: flex-end; gap: 6px; }"] });
|
|
454
|
+
};
|
|
455
|
+
ThemeManagerDashboardComponent = __decorate([
|
|
456
|
+
RegisterClass(BaseDashboard, 'ThemeManagerDashboard')
|
|
457
|
+
], ThemeManagerDashboardComponent);
|
|
458
|
+
export { ThemeManagerDashboardComponent };
|
|
459
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ThemeManagerDashboardComponent, [{
|
|
460
|
+
type: Component,
|
|
461
|
+
args: [{ standalone: false, selector: 'mj-theme-manager-dashboard', template: "<mj-page-layout>\n <mj-page-header Title=\"Manage Themes\" Icon=\"fa-solid fa-swatchbook\"\n Subtitle=\"Your organization's saved brand themes\"></mj-page-header>\n\n <mj-page-body>\n @if (!themes.length) {\n <mj-empty-state Icon=\"fa-solid fa-swatchbook\" Title=\"No themes yet\"\n Message=\"Create one in the Theme Studio tab.\"></mj-empty-state>\n } @else {\n <table class=\"tm-table\">\n <thead>\n <tr><th>Theme</th><th>Status</th><th>Default</th><th class=\"r\">Actions</th></tr>\n </thead>\n <tbody>\n @for (t of themes; track t.id) {\n <tr>\n <td class=\"name-cell\">\n <button type=\"button\" class=\"star-btn\" [class.on]=\"t.starred\"\n [title]=\"t.starred ? 'Unstar' : 'Star (adds to your user menu)'\" (click)=\"toggleStar(t)\">\n <i [class]=\"t.starred ? 'fa-solid fa-star' : 'fa-regular fa-star'\"></i>\n </button>\n <span class=\"sw\">\n @for (c of t.swatches; track $index) { <i [style.background]=\"c\"></i> }\n </span>\n <b>{{ t.name }}</b>\n @if (t.builtIn) { <span class=\"builtin-badge\"><i class=\"fa-solid fa-lock\"></i> Built-in</span> }\n </td>\n <td><span class=\"status-pill\" [class.inactive]=\"t.status !== 'Active'\">{{ t.status }}</span></td>\n <td>\n @if (t.isDefault) {\n <span class=\"badge\">DEFAULT</span>\n } @else {\n <button type=\"button\" class=\"link-btn\" title=\"Make this the organization-wide default\" (click)=\"setDefault(t)\">Set as org default</button>\n }\n </td>\n <td class=\"r actions\">\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"isApplied(t.id)\"\n title=\"Apply this theme to your workspace now\" (click)=\"applyToMe(t)\">\n {{ isApplied(t.id) ? 'Applied' : 'Apply' }}\n </button>\n <button mjButton variant=\"outline\" size=\"sm\" (click)=\"duplicate(t)\">Duplicate</button>\n @if (!t.builtIn) {\n <button mjButton variant=\"outline\" size=\"sm\" (click)=\"edit(t)\">Edit</button>\n <button mjButton variant=\"danger\" size=\"sm\" (click)=\"remove(t)\">Delete</button>\n }\n </td>\n </tr>\n }\n </tbody>\n </table>\n }\n </mj-page-body>\n</mj-page-layout>\n", styles: ["/* Theme Manager \u2014 page chrome comes from the shared trio (mj-page-layout/-header/-body);\n only the themes table and its cell accents live here. All colors are --mj-* tokens. */\n:host { display: block; width: 100%; height: 100%; }\n\n.tm-table { width: 100%; max-width: 1100px; margin: 0 auto; border-collapse: collapse; background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default); border-radius: 12px; overflow: hidden; box-shadow: var(--mj-shadow-sm); }\n.tm-table th { text-align: left; font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--mj-text-muted); background: var(--mj-bg-surface-card); padding: 11px 16px; border-bottom: 1px solid var(--mj-border-default); }\n.tm-table th.r, .tm-table td.r { text-align: right; }\n.tm-table td { padding: 11px 16px; border-bottom: 1px solid var(--mj-border-subtle); font-size: 13px; color: var(--mj-text-secondary); vertical-align: middle; }\n.tm-table tr:last-child td { border-bottom: 0; }\n.name-cell { display: flex; align-items: center; gap: 10px; }\n.star-btn { border: 0; background: transparent; color: var(--mj-text-muted); cursor: pointer; font-size: 15px; padding: 2px; line-height: 1; }\n.star-btn:hover { color: var(--mj-status-warning); }\n.star-btn.on { color: var(--mj-status-warning); }\n.name-cell b { color: var(--mj-text-primary); font-weight: 600; }\n.sw { display: flex; flex-shrink: 0; }\n.sw i { width: 12px; height: 20px; display: block; margin-left: -4px; border-radius: 3px; border: 1.5px solid var(--mj-bg-surface); }\n.sw i:first-child { margin-left: 0; }\n.badge { font-size: 9.5px; font-weight: 700; letter-spacing: .04em; color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 14%, transparent); padding: 2px 6px; border-radius: 20px; }\n.status-pill { font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 20px; background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.status-pill.inactive { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n.builtin-badge { display: inline-flex; align-items: center; gap: 4px; font-size: 10px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--mj-text-secondary); background: var(--mj-bg-surface-sunken); border: 1px solid var(--mj-border-default); padding: 2px 7px; border-radius: 20px; }\n.link-btn { border: 0; background: transparent; color: var(--mj-text-link); font: inherit; font-size: 12.5px; cursor: pointer; padding: 0; }\n.actions { display: flex; align-items: center; justify-content: flex-end; gap: 6px; }\n"] }]
|
|
462
|
+
}], () => [{ type: i0.ChangeDetectorRef }, { type: i1.ThemeService }, { type: i2.MJConfirmService }], null); })();
|
|
463
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ThemeManagerDashboardComponent, { className: "ThemeManagerDashboardComponent", filePath: "src/ThemeStudio/theme-manager-dashboard.component.ts", lineNumber: 43 }); })();
|
|
464
|
+
//# sourceMappingURL=theme-manager-dashboard.component.js.map
|